mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-09 03:50:49 +00:00
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:
parent
462b7a2256
commit
bf04275478
5 changed files with 56 additions and 12 deletions
|
@ -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',
|
||||
|
|
Loading…
Add table
Reference in a new issue