57 lines
1.7 KiB
Go
57 lines
1.7 KiB
Go
// Package antiabuse provides runtime in-process adapters for auth-specific
|
|
// public abuse controls.
|
|
package antiabuse
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"galaxy/authsession/internal/domain/challenge"
|
|
"galaxy/authsession/internal/domain/common"
|
|
"galaxy/authsession/internal/ports"
|
|
)
|
|
|
|
// SendEmailCodeProtector is a concurrency-safe in-process resend-throttle
|
|
// adapter for public send-email-code attempts.
|
|
type SendEmailCodeProtector struct {
|
|
mu sync.Mutex
|
|
reservedUntil map[common.Email]time.Time
|
|
}
|
|
|
|
// CheckAndReserve applies the fixed Stage-17 resend cooldown using input.Now
|
|
// as the authoritative decision timestamp.
|
|
func (p *SendEmailCodeProtector) CheckAndReserve(ctx context.Context, input ports.SendEmailCodeAbuseInput) (ports.SendEmailCodeAbuseResult, error) {
|
|
if ctx == nil {
|
|
return ports.SendEmailCodeAbuseResult{}, fmt.Errorf("check and reserve send email code abuse: nil context")
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
return ports.SendEmailCodeAbuseResult{}, err
|
|
}
|
|
if err := input.Validate(); err != nil {
|
|
return ports.SendEmailCodeAbuseResult{}, fmt.Errorf("check and reserve send email code abuse: %w", err)
|
|
}
|
|
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
if p.reservedUntil == nil {
|
|
p.reservedUntil = make(map[common.Email]time.Time)
|
|
}
|
|
|
|
reservedUntil, exists := p.reservedUntil[input.Email]
|
|
if exists && input.Now.Before(reservedUntil) {
|
|
return ports.SendEmailCodeAbuseResult{
|
|
Outcome: ports.SendEmailCodeAbuseOutcomeThrottled,
|
|
}, nil
|
|
}
|
|
|
|
p.reservedUntil[input.Email] = input.Now.UTC().Add(challenge.ResendThrottleCooldown)
|
|
return ports.SendEmailCodeAbuseResult{
|
|
Outcome: ports.SendEmailCodeAbuseOutcomeAllowed,
|
|
}, nil
|
|
}
|
|
|
|
var _ ports.SendEmailCodeAbuseProtector = (*SendEmailCodeProtector)(nil)
|