Disable sensitive challenge rate limit for test
This commit is contained in:
+38
-35
@@ -57,19 +57,20 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
imVerificationCodeLength = 6
|
imVerificationCodeLength = 6
|
||||||
imVerificationExpiresInSeconds = 300
|
imVerificationExpiresInSeconds = 300
|
||||||
imVerificationResendAfterSeconds = 60
|
imVerificationResendAfterSeconds = 60
|
||||||
imVerificationMaxAttempts = 5
|
imVerificationMaxAttempts = 5
|
||||||
imVerificationCreateRateWindow = time.Hour
|
imVerificationCreateRateWindow = time.Hour
|
||||||
imActionTokenExpiresInSeconds = 300
|
imSensitiveChallengeRateLimitEnabled = false
|
||||||
imIndependentPasswordMaxAttempts = 5
|
imActionTokenExpiresInSeconds = 300
|
||||||
imIndependentPasswordLockSeconds = 300
|
imIndependentPasswordMaxAttempts = 5
|
||||||
imQRCodeDefaultExpiresInSeconds = 86400
|
imIndependentPasswordLockSeconds = 300
|
||||||
imQRCodeMaxExpiresInSeconds = 7 * 86400
|
imQRCodeDefaultExpiresInSeconds = 86400
|
||||||
imPublicUploadMaxBytes = 10 << 20
|
imQRCodeMaxExpiresInSeconds = 7 * 86400
|
||||||
groupMaxAdminCount = 10
|
imPublicUploadMaxBytes = 10 << 20
|
||||||
reportPreviewMaxRunes = 120
|
groupMaxAdminCount = 10
|
||||||
|
reportPreviewMaxRunes = 120
|
||||||
)
|
)
|
||||||
|
|
||||||
type imHTTPResponse struct {
|
type imHTTPResponse struct {
|
||||||
@@ -6245,29 +6246,31 @@ func currentUserPhone(tenantID types.TenantID, uid types.Uid) (string, error) {
|
|||||||
func createSensitiveChallenge(wrt http.ResponseWriter, req *http.Request, principal *imPrincipal, action, phone string) {
|
func createSensitiveChallenge(wrt http.ResponseWriter, req *http.Request, principal *imPrincipal, action, phone string) {
|
||||||
now := time.Now().UTC().Round(time.Millisecond)
|
now := time.Now().UTC().Round(time.Millisecond)
|
||||||
phoneHash := hashIMSecret(principal.TenantID, "sensitive_action", phone, "phone")
|
phoneHash := hashIMSecret(principal.TenantID, "sensitive_action", phone, "phone")
|
||||||
active, err := store.Verifications.GetActive(principal.TenantID, "sensitive_action", phoneHash, now)
|
if imSensitiveChallengeRateLimitEnabled {
|
||||||
if err != nil && !errors.Is(err, types.ErrNotFound) {
|
active, err := store.Verifications.GetActive(principal.TenantID, "sensitive_action", phoneHash, now)
|
||||||
writeIMError(wrt, req, http.StatusInternalServerError, 90000, imReasonInternal, "internal error", nil)
|
if err != nil && !errors.Is(err, types.ErrNotFound) {
|
||||||
return
|
writeIMError(wrt, req, http.StatusInternalServerError, 90000, imReasonInternal, "internal error", nil)
|
||||||
}
|
return
|
||||||
if active != nil && now.Before(active.ResendAfter) {
|
}
|
||||||
writeIMError(wrt, req, http.StatusTooManyRequests, 30020, imReasonVerificationRate, "verification rate limited", map[string]any{
|
if active != nil && now.Before(active.ResendAfter) {
|
||||||
"challenge_id": active.ChallengeID,
|
writeIMError(wrt, req, http.StatusTooManyRequests, 30020, imReasonVerificationRate, "verification rate limited", map[string]any{
|
||||||
"retry_after_seconds": int(time.Until(active.ResendAfter).Seconds()) + 1,
|
"challenge_id": active.ChallengeID,
|
||||||
})
|
"retry_after_seconds": int(time.Until(active.ResendAfter).Seconds()) + 1,
|
||||||
return
|
})
|
||||||
}
|
return
|
||||||
if allowed, _, ttl, err := store.AllowRate(principal.TenantID, "verification:create:sensitive_action:"+phoneHash, imVerificationMaxAttempts, imVerificationCreateRateWindow); err != nil {
|
}
|
||||||
logs.Warn.Println("im sensitive verification create: redis rate limit failed", principal.TenantID, err)
|
if allowed, _, ttl, err := store.AllowRate(principal.TenantID, "verification:create:sensitive_action:"+phoneHash, imVerificationMaxAttempts, imVerificationCreateRateWindow); err != nil {
|
||||||
} else if !allowed {
|
logs.Warn.Println("im sensitive verification create: redis rate limit failed", principal.TenantID, err)
|
||||||
retryAfter := int(ttl.Seconds())
|
} else if !allowed {
|
||||||
if retryAfter < 1 {
|
retryAfter := int(ttl.Seconds())
|
||||||
retryAfter = int(imVerificationCreateRateWindow.Seconds())
|
if retryAfter < 1 {
|
||||||
|
retryAfter = int(imVerificationCreateRateWindow.Seconds())
|
||||||
|
}
|
||||||
|
writeIMError(wrt, req, http.StatusTooManyRequests, 30020, imReasonVerificationRate, "verification rate limited", map[string]any{
|
||||||
|
"retry_after_seconds": retryAfter,
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
writeIMError(wrt, req, http.StatusTooManyRequests, 30020, imReasonVerificationRate, "verification rate limited", map[string]any{
|
|
||||||
"retry_after_seconds": retryAfter,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
challengeID, err := randomIMToken(18)
|
challengeID, err := randomIMToken(18)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2194,12 +2194,13 @@ func TestServeIMSecurityChallengeVerifyIssuesActionToken(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServeIMSecurityChallengeCreateReturnsActiveChallengeBeforeRateLimit(t *testing.T) {
|
func TestServeIMSecurityChallengeCreateSkipsRateLimitWhenDisabled(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
expectIMBearer(t, ctrl, "token-1", 7, 42)
|
expectIMBearer(t, ctrl, "token-1", 7, 42)
|
||||||
oldUsers := store.Users
|
oldUsers := store.Users
|
||||||
oldVerifications := store.Verifications
|
oldVerifications := store.Verifications
|
||||||
|
oldSender := sendIMVerificationCode
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
users := mock_store.NewMockUsersPersistenceInterface(ctrl)
|
users := mock_store.NewMockUsersPersistenceInterface(ctrl)
|
||||||
users.EXPECT().Get(types.TenantID(7), types.Uid(42)).Return(&types.User{
|
users.EXPECT().Get(types.TenantID(7), types.Uid(42)).Return(&types.User{
|
||||||
@@ -2220,9 +2221,11 @@ func TestServeIMSecurityChallengeCreateReturnsActiveChallengeBeforeRateLimit(t *
|
|||||||
}}
|
}}
|
||||||
store.Users = users
|
store.Users = users
|
||||||
store.Verifications = verifications
|
store.Verifications = verifications
|
||||||
|
sendIMVerificationCode = func(_ types.TenantID, _, _, _, _ string) error { return nil }
|
||||||
defer func() {
|
defer func() {
|
||||||
store.Users = oldUsers
|
store.Users = oldUsers
|
||||||
store.Verifications = oldVerifications
|
store.Verifications = oldVerifications
|
||||||
|
sendIMVerificationCode = oldSender
|
||||||
}()
|
}()
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodPost, "/v1/im/security/challenges",
|
req := httptest.NewRequest(http.MethodPost, "/v1/im/security/challenges",
|
||||||
@@ -2232,15 +2235,11 @@ func TestServeIMSecurityChallengeCreateReturnsActiveChallengeBeforeRateLimit(t *
|
|||||||
|
|
||||||
serveIMSecurityChallenges(rec, req)
|
serveIMSecurityChallenges(rec, req)
|
||||||
|
|
||||||
if rec.Code != http.StatusTooManyRequests {
|
if rec.Code != http.StatusOK {
|
||||||
t.Fatalf("status: got %d, want %d; body=%s", rec.Code, http.StatusTooManyRequests, rec.Body.String())
|
t.Fatalf("status: got %d, want %d; body=%s", rec.Code, http.StatusOK, rec.Body.String())
|
||||||
}
|
}
|
||||||
data := decodeIMHTTPResponse(t, rec).Details.(map[string]any)
|
if verifications.created == nil || verifications.created.Action != "close_youth_mode" {
|
||||||
if data["challenge_id"] != "challenge-active" {
|
t.Fatalf("expected a new challenge while sensitive challenge rate limit is disabled: %#v", verifications.created)
|
||||||
t.Fatalf("expected active challenge id, got %#v", data)
|
|
||||||
}
|
|
||||||
if verifications.created != nil {
|
|
||||||
t.Fatalf("should not create a new challenge while resend is cooling down: %#v", verifications.created)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user