refactor: change config place and visibility
Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
Vendored
+17
-14
@@ -34,21 +34,24 @@ export interface Config {
|
||||
* unless you configure credentials for service calls.
|
||||
*/
|
||||
dangerouslyDisableDefaultAuthPolicy?: boolean;
|
||||
rateLimit?:
|
||||
| false
|
||||
| {
|
||||
/**
|
||||
* Limit each IP to max requests per window
|
||||
*/
|
||||
max?: number;
|
||||
/**
|
||||
* The duration for which the rate limit is enforced
|
||||
*/
|
||||
window?: HumanDuration;
|
||||
|
||||
disable?: true;
|
||||
};
|
||||
};
|
||||
/** @visibility frontend */
|
||||
rateLimit?:
|
||||
| false
|
||||
| {
|
||||
/**
|
||||
* Limit each IP to max requests per window, defaults to 60 requests.
|
||||
*/
|
||||
max?: number;
|
||||
/**
|
||||
* The duration for which the rate limit is enforced, defaults to 1 minute.
|
||||
*/
|
||||
window?: HumanDuration;
|
||||
/**
|
||||
* Disable the rate limit verification.
|
||||
*/
|
||||
disable?: true;
|
||||
};
|
||||
};
|
||||
|
||||
/** Discovery options. */
|
||||
|
||||
+12
-14
@@ -188,8 +188,8 @@ it('do not limit authenticated requests', async () => {
|
||||
const max = 2;
|
||||
const configMock = {
|
||||
backend: {
|
||||
auth: {
|
||||
rateLimit: {
|
||||
rateLimit: {
|
||||
unauthorized: {
|
||||
max, // 2 requests per window
|
||||
window: { minutes: 2 }, // rate limit window expiration time,
|
||||
},
|
||||
@@ -237,8 +237,8 @@ it('limit the number of unauthenticated requests', async () => {
|
||||
|
||||
const configMock = {
|
||||
backend: {
|
||||
auth: {
|
||||
rateLimit: {
|
||||
rateLimit: {
|
||||
unauthorized: {
|
||||
max: 2, // 2 requests per window
|
||||
window: { minutes: 2 }, // rate limit window expiration time,
|
||||
},
|
||||
@@ -279,25 +279,25 @@ it('limit the number of unauthenticated requests', async () => {
|
||||
expect(cacheMock.get).toHaveBeenCalledTimes(4);
|
||||
expect(cacheMock.set).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
`rl_${randomIp}`, // rl is the default prefix for the rate limit store
|
||||
`unauthorized_rate_limit_${randomIp}`, // unauthorized_rate_limit_ is the default prefix for the rate limit store
|
||||
{ resetTime, totalHits: 1 },
|
||||
{ ttl: twoMinutesInMilliseconds },
|
||||
);
|
||||
expect(cacheMock.set).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
`rl_${randomIp}`,
|
||||
`unauthorized_rate_limit_${randomIp}`,
|
||||
{ resetTime, totalHits: 2 },
|
||||
{ ttl: twoMinutesInMilliseconds },
|
||||
);
|
||||
expect(cacheMock.set).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
`rl_${randomIp}`,
|
||||
`unauthorized_rate_limit_${randomIp}`,
|
||||
{ resetTime, totalHits: 3 },
|
||||
{ ttl: twoMinutesInMilliseconds },
|
||||
);
|
||||
expect(cacheMock.set).toHaveBeenNthCalledWith(
|
||||
4,
|
||||
`rl_${randomIp}`,
|
||||
`unauthorized_rate_limit_${randomIp}`,
|
||||
{ resetTime, totalHits: 1 },
|
||||
{ ttl: twoMinutesInMilliseconds },
|
||||
);
|
||||
@@ -320,8 +320,8 @@ it('skip limiting requests when the rate limit is disabled', async () => {
|
||||
cache: cacheMock,
|
||||
config: {
|
||||
backend: {
|
||||
auth: {
|
||||
rateLimit: {
|
||||
rateLimit: {
|
||||
unauthorized: {
|
||||
max,
|
||||
window: { minutes: 2 },
|
||||
disabled: true,
|
||||
@@ -333,7 +333,6 @@ it('skip limiting requests when the rate limit is disabled', async () => {
|
||||
|
||||
barrier1.addAuthPolicy({ allow: 'unauthenticated', path: '/public' });
|
||||
|
||||
// exceed the rate limit for authenticated requests
|
||||
for (let i = 0; i < max + 1; i += 1) {
|
||||
await request(app1)
|
||||
.get('/public')
|
||||
@@ -349,8 +348,8 @@ it('skip limiting requests when the rate limit is disabled', async () => {
|
||||
cache: cacheMock,
|
||||
config: {
|
||||
backend: {
|
||||
auth: {
|
||||
rateLimit: false,
|
||||
rateLimit: {
|
||||
unauthorized: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -358,7 +357,6 @@ it('skip limiting requests when the rate limit is disabled', async () => {
|
||||
|
||||
barrier2.addAuthPolicy({ allow: 'unauthenticated', path: '/public' });
|
||||
|
||||
// exceed the rate limit for authenticated requests
|
||||
await request(app2).get('/public').send().expect(200);
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
+7
-6
@@ -65,18 +65,19 @@ export function createCredentialsBarrier(options: {
|
||||
const unauthenticatedPredicates = new Array<(path: string) => boolean>();
|
||||
const cookiePredicates = new Array<(path: string) => boolean>();
|
||||
|
||||
const rateLimitConfig = config.getOptional('backend.auth.rateLimit');
|
||||
const rateLimitConfig = config.getOptional('backend.rateLimit.unauthorized');
|
||||
|
||||
const disabled =
|
||||
rateLimitConfig === false ||
|
||||
(typeof rateLimitConfig === 'object' &&
|
||||
config?.getOptionalBoolean('backend.auth.rateLimit.disabled') === true);
|
||||
config?.getOptionalBoolean('backend.rateLimit.unauthorized.disabled') ===
|
||||
true);
|
||||
|
||||
const duration =
|
||||
typeof rateLimitConfig === 'object' &&
|
||||
config?.has('backend.auth.rateLimit.window')
|
||||
config?.has('backend.rateLimit.unauthorized.window')
|
||||
? readDurationFromConfig(
|
||||
config.getConfig('backend.auth.rateLimit.window'),
|
||||
config.getConfig('backend.rateLimit.unauthorized.window'),
|
||||
)
|
||||
: undefined;
|
||||
|
||||
@@ -84,8 +85,8 @@ export function createCredentialsBarrier(options: {
|
||||
|
||||
const max =
|
||||
typeof rateLimitConfig === 'object' &&
|
||||
config?.has('backend.auth.rateLimit.max')
|
||||
? config.getNumber('backend.auth.rateLimit.max')
|
||||
config?.has('backend.rateLimit.unauthorized.max')
|
||||
? config.getNumber('backend.rateLimit.unauthorized.max')
|
||||
: 60;
|
||||
|
||||
// Default rate limit is 60 requests per 1 minute
|
||||
|
||||
+5
-2
@@ -18,13 +18,12 @@ import { ServiceMock, mockServices } from '@backstage/backend-test-utils';
|
||||
import { RateLimitStore } from './rateLimitStore';
|
||||
import { CacheService } from '@backstage/backend-plugin-api';
|
||||
|
||||
jest.setSystemTime(Date.parse('2024-03-15T08:39:11.869Z'));
|
||||
|
||||
describe('RateLimitStore', () => {
|
||||
let cacheServiceMock: ServiceMock<CacheService>;
|
||||
let rateLimitStore: RateLimitStore;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers({ now: Date.parse('2024-03-15T08:39:11.869Z') });
|
||||
jest.clearAllMocks();
|
||||
cacheServiceMock = mockServices.cache.mock();
|
||||
rateLimitStore = RateLimitStore.fromOptions({
|
||||
@@ -33,6 +32,10 @@ describe('RateLimitStore', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should initialize with default options', () => {
|
||||
expect(rateLimitStore.windowMs).toBe(1 * 60 * 1000);
|
||||
});
|
||||
|
||||
@@ -51,7 +51,7 @@ export class RateLimitStore implements Store {
|
||||
#cache: CacheService;
|
||||
|
||||
private constructor(options: CacheStoreOptions) {
|
||||
this.prefix = options.prefix ?? 'rl_';
|
||||
this.prefix = options.prefix ?? 'unauthorized_rate_limit_';
|
||||
this.#cache = options.cache;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user