Merge pull request #14090 from overleaf/bg-best-reduce-rate-limiter-on-confirmation-email-resend

reduce rate limiter on confirmation emails

GitOrigin-RevId: 87743dd9dac483a68ff82f1185ae1156d60b0575
This commit is contained in:
Brian Gough
2023-08-31 08:04:05 +00:00
committed by Copybot
parent 462b7a2256
commit bf04275478
5 changed files with 56 additions and 12 deletions
+1 -1
View File
@@ -187,7 +187,7 @@ const rateLimiters = {
duration: 60,
}),
resendConfirmation: new RateLimiter('resend-confirmation', {
points: 10,
points: 1,
duration: 60,
}),
sendChatMessage: new RateLimiter('send-chat-message', {
@@ -4,7 +4,10 @@ import { Button } from 'react-bootstrap'
import Icon from '../../../../../../shared/components/icon'
import getMeta from '../../../../../../utils/meta'
import useAsync from '../../../../../../shared/hooks/use-async'
import { postJSON } from '../../../../../../infrastructure/fetch-json'
import {
FetchError,
postJSON,
} from '../../../../../../infrastructure/fetch-json'
import { UserEmailData } from '../../../../../../../../types/user-email'
import { ExposedSettings } from '../../../../../../../../types/exposed-settings'
import { Institution } from '../../../../../../../../types/institution'
@@ -21,7 +24,7 @@ function ReconfirmAffiliation({
}: ReconfirmAffiliationProps) {
const { t } = useTranslation()
const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings
const { isLoading, isError, isSuccess, runAsync } = useAsync()
const { error, isLoading, isError, isSuccess, runAsync } = useAsync()
const [hasSent, setHasSent] = useState(false)
const [isPending, setIsPending] = useState(false)
const ssoEnabled = institution.ssoEnabled
@@ -48,6 +51,9 @@ function ReconfirmAffiliation({
}
}
const rateLimited =
error && error instanceof FetchError && error.response?.status === 429
if (hasSent) {
return (
<div className="w-100">
@@ -73,7 +79,11 @@ function ReconfirmAffiliation({
{isError && (
<>
<br />
<div>{t('generic_something_went_wrong')}</div>
<div>
{rateLimited
? t('too_many_requests')
: t('generic_something_went_wrong')}
</div>
</>
)}
</div>
@@ -116,7 +126,11 @@ function ReconfirmAffiliation({
{isError && (
<>
<br />
<div>{t('generic_something_went_wrong')}</div>
<div>
{rateLimited
? t('too_many_requests')
: t('generic_something_went_wrong')}
</div>
</>
)}
</div>
@@ -1,6 +1,6 @@
import { useState, useEffect, useLayoutEffect } from 'react'
import useAsync from '../../../../../shared/hooks/use-async'
import { postJSON } from '../../../../../infrastructure/fetch-json'
import { FetchError, postJSON } from '../../../../../infrastructure/fetch-json'
import { Trans, useTranslation } from 'react-i18next'
import { Institution } from '../../../../../../../types/institution'
import { Button } from 'react-bootstrap'
@@ -24,7 +24,7 @@ function ReconfirmationInfoPrompt({
}: ReconfirmationInfoPromptProps) {
const { t } = useTranslation()
const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings
const { isLoading, isError, isSuccess, runAsync } = useAsync()
const { error, isLoading, isError, isSuccess, runAsync } = useAsync()
const { state, setLoading: setUserEmailsContextLoading } =
useUserEmailsContext()
const [isPending, setIsPending] = useState(false)
@@ -59,6 +59,9 @@ function ReconfirmationInfoPrompt({
}
}
const rateLimited =
isError && error instanceof FetchError && error.response?.status === 429
if (hasSent) {
return (
<div>
@@ -87,7 +90,11 @@ function ReconfirmationInfoPrompt({
)}
<br />
{isError && (
<div className="text-danger">{t('generic_something_went_wrong')}</div>
<div className="text-danger">
{rateLimited
? t('too_many_requests')
: t('generic_something_went_wrong')}
</div>
)}
</div>
)
@@ -117,7 +124,11 @@ function ReconfirmationInfoPrompt({
</Button>
<br />
{isError && (
<div className="text-danger">{t('generic_something_went_wrong')}</div>
<div className="text-danger">
{rateLimited
? t('too_many_requests')
: t('generic_something_went_wrong')}
</div>
)}
</div>
</>
@@ -2,7 +2,7 @@ import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import Icon from '../../../../shared/components/icon'
import { Button } from 'react-bootstrap'
import { postJSON } from '../../../../infrastructure/fetch-json'
import { FetchError, postJSON } from '../../../../infrastructure/fetch-json'
import useAsync from '../../../../shared/hooks/use-async'
import { UserEmailData } from '../../../../../../types/user-email'
import { useUserEmailsContext } from '../../context/user-email-context'
@@ -15,7 +15,7 @@ function ResendConfirmationEmailButton({
email,
}: ResendConfirmationEmailButtonProps) {
const { t } = useTranslation()
const { isLoading, isError, runAsync } = useAsync()
const { error, isLoading, isError, runAsync } = useAsync()
const { state, setLoading: setUserEmailsContextLoading } =
useUserEmailsContext()
@@ -42,6 +42,9 @@ function ResendConfirmationEmailButton({
)
}
const rateLimited =
error && error instanceof FetchError && error.response?.status === 429
return (
<>
<Button
@@ -54,7 +57,11 @@ function ResendConfirmationEmailButton({
</Button>
<br />
{isError && (
<div className="text-danger">{t('generic_something_went_wrong')}</div>
<div className="text-danger">
{rateLimited
? t('too_many_requests')
: t('generic_something_went_wrong')}
</div>
)}
</>
)
@@ -13,6 +13,16 @@ const {
UserAuditLogEntry,
} = require('../../../../app/src/models/UserAuditLogEntry')
// Import the rate limiter so we can clear it between tests
const {
RateLimiter,
} = require('../../../../app/src/infrastructure/RateLimiter')
const rateLimiters = {
resendConfirmation: new RateLimiter('resend-confirmation'),
}
let globalUserNum = Settings.test.counterInit
class UserHelper {
@@ -438,6 +448,8 @@ class UserHelper {
}
async confirmEmail(userId, email) {
// clear ratelimiting on resend confirmation endpoint
await rateLimiters.resendConfirmation.delete(userId)
// UserHelper.createUser does not create a confirmation token
let response = await this.fetch('/user/emails/resend_confirmation', {
method: 'POST',