Skip to content

Commit

Permalink
Added login endpoint rate limit (#2074)
Browse files Browse the repository at this point in the history
* Added login rate limit

* Make more pretty

* Make more pretty

* Fix

* Remove double after all
  • Loading branch information
sjaanus committed Sep 26, 2022
1 parent 74c5db6 commit 1426d5b
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 1 deletion.
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -99,6 +99,7 @@
"deepmerge": "^4.2.2",
"errorhandler": "^1.5.1",
"express": "^4.17.1",
"express-rate-limit": "^6.6.0",
"express-session": "^1.17.1",
"fast-json-patch": "^3.1.0",
"gravatar-url": "^3.1.0",
Expand Down
5 changes: 5 additions & 0 deletions src/lib/routes/controller.ts
Expand Up @@ -179,6 +179,11 @@ export default class Controller {
this.app.use(path, router);
}

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
useWithMiddleware(path: string, router: IRouter, middleware: any): void {
this.app.use(path, middleware, router);
}

get router(): any {
return this.app;
}
Expand Down
9 changes: 8 additions & 1 deletion src/lib/routes/index.ts
Expand Up @@ -3,6 +3,7 @@ import ResetPasswordController from './auth/reset-password-controller';
import { SimplePasswordProvider } from './auth/simple-password-provider';
import { IUnleashConfig, IUnleashServices } from '../types';
import LogoutController from './logout';
import rateLimit from 'express-rate-limit';

const AdminApi = require('./admin-api');
const ClientApi = require('./client-api');
Expand All @@ -19,9 +20,15 @@ class IndexRouter extends Controller {
this.use('/health', new HealthCheckController(config, services).router);
this.use('/internal-backstage', new BackstageController(config).router);
this.use('/logout', new LogoutController(config, services).router);
this.use(
this.useWithMiddleware(
'/auth/simple',
new SimplePasswordProvider(config, services).router,
rateLimit({
windowMs: 1 * 60 * 1000,
max: 5,
standardHeaders: true,
legacyHeaders: false,
}),
);
this.use(
'/auth/reset',
Expand Down
85 changes: 85 additions & 0 deletions src/test/e2e/api/auth/simple-password-provider.e2e.test.ts
@@ -0,0 +1,85 @@
import { createTestConfig } from '../../../config/test-config';
import { IUnleashConfig } from '../../../../lib/types';
import UserService from '../../../../lib/services/user-service';
import { AccessService } from '../../../../lib/services/access-service';
import { IUser } from '../../../../lib/types/user';
import { setupApp } from '../../helpers/test-helper';
import dbInit from '../../helpers/database-init';
import getLogger from '../../../fixtures/no-logger';
import { EmailService } from '../../../../lib/services/email-service';
import SessionService from '../../../../lib/services/session-service';
import { RoleName } from '../../../../lib/types/model';
import SettingService from '../../../../lib/services/setting-service';
import { GroupService } from '../../../../lib/services/group-service';
import ResetTokenService from '../../../../lib/services/reset-token-service';

let app;
let stores;
let db;
const config: IUnleashConfig = createTestConfig({
getLogger,
server: {
unleashUrl: 'http://localhost:3000',
baseUriPath: '',
},
email: {
host: 'test',
},
});
const password = 'DtUYwi&l5I1KX4@Le';
let userService: UserService;
let adminUser: IUser;

beforeAll(async () => {
db = await dbInit('simple_password_provider_api_serial', getLogger);
stores = db.stores;
app = await setupApp(stores);
const groupService = new GroupService(stores, config);
const accessService = new AccessService(stores, config, groupService);
const resetTokenService = new ResetTokenService(stores, config);
const emailService = new EmailService(undefined, config.getLogger);
const sessionService = new SessionService(stores, config);
const settingService = new SettingService(stores, config);

userService = new UserService(stores, config, {
accessService,
resetTokenService,
emailService,
sessionService,
settingService,
});
const adminRole = await accessService.getRootRole(RoleName.ADMIN);
adminUser = await userService.createUser({
username: 'admin@test.com',
email: 'admin@test.com',
rootRole: adminRole.id,
password: password,
});
});

afterAll(async () => {
await app.destroy();
await db.destroy();
});

test('Can log in', async () => {
await app.request
.post('/auth/simple/login')
.send({
username: adminUser.username,
password,
})
.expect(200);
});

test('Gets rate limited after 5 tries', async () => {
for (let statusCode of [200, 200, 200, 200, 429]) {
await app.request
.post('/auth/simple/login')
.send({
username: adminUser.username,
password,
})
.expect(statusCode);
}
});
5 changes: 5 additions & 0 deletions yarn.lock
Expand Up @@ -3190,6 +3190,11 @@ expect@^29.0.0, expect@^29.0.1:
jest-message-util "^29.0.1"
jest-util "^29.0.1"

express-rate-limit@^6.6.0:
version "6.6.0"
resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-6.6.0.tgz#3bbc2546540d327b1b0bfa9ab5f1b2c49075af98"
integrity sha512-HFN2+4ZGdkQOS8Qli4z6knmJFnw6lZed67o6b7RGplWeb1Z0s8VXaj3dUgPIdm9hrhZXTRpCTHXA0/2Eqex0vA==

express-session@^1.17.1:
version "1.17.2"
resolved "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz"
Expand Down

0 comments on commit 1426d5b

Please sign in to comment.