Your current remaining daily quota is:
- {{$quotaRemaining}}/{{$quotaLimit}}. This value resets at
- midnight UTC.
+ {{$quotaRemaining}}/{{$quotaLimit}}.
+ This value is calculated and reset each day at 11:59:59 UTC.
+
- {{if gt $quotaRemaining $quotaLimit}}
- If your remaining quota exceeds the maximum quota, it means a realm
+ {{if gt $quotaRemaining $quotaLimit}}
+
+ Your remaining quota exceeds the maximum quota, meaning a realm
administrator added a temporary burst.
- {{end}}
-
+
+ {{end}}
diff --git a/pkg/config/admin_server_config.go b/pkg/config/admin_server_config.go
index 051a4b5c7..8c8a54e54 100644
--- a/pkg/config/admin_server_config.go
+++ b/pkg/config/admin_server_config.go
@@ -48,7 +48,7 @@ type AdminAPIServerConfig struct {
CollisionRetryCount uint `env:"COLLISION_RETRY_COUNT,default=6"`
AllowedSymptomAge time.Duration `env:"ALLOWED_PAST_SYMPTOM_DAYS,default=336h"` // 336h is 14 days.
- EnforceRealmQuotas bool `env:"ENFORCE_REALM_QUOTAS, default=false"`
+ EnforceRealmQuotas bool `env:"ENFORCE_REALM_QUOTAS, default=true"`
// For EN Express, the link will be
// https://[realm-region].[ENX_REDIRECT_DOMAIN]/v?c=[longcode]
diff --git a/pkg/config/server_config.go b/pkg/config/server_config.go
index 3622250c6..ed54f78e4 100644
--- a/pkg/config/server_config.go
+++ b/pkg/config/server_config.go
@@ -77,7 +77,7 @@ type ServerConfig struct {
ServerName string `env:"SERVER_NAME,default=Diagnosis Verification Server"`
CollisionRetryCount uint `env:"COLLISION_RETRY_COUNT,default=6"`
AllowedSymptomAge time.Duration `env:"ALLOWED_PAST_SYMPTOM_DAYS,default=336h"` // 336h is 14 days.
- EnforceRealmQuotas bool `env:"ENFORCE_REALM_QUOTAS, default=false"`
+ EnforceRealmQuotas bool `env:"ENFORCE_REALM_QUOTAS, default=true"`
AssetsPath string `env:"ASSETS_PATH,default=./cmd/server/assets"`
diff --git a/pkg/controller/realmadmin/settings.go b/pkg/controller/realmadmin/settings.go
index 708348ccb..2e88fe523 100644
--- a/pkg/controller/realmadmin/settings.go
+++ b/pkg/controller/realmadmin/settings.go
@@ -199,11 +199,38 @@ func (c *Controller) HandleSettings() http.Handler {
}
// Abuse prevention
+ var abusePreventionJustEnabled bool
if form.AbusePrevention {
+ abusePreventionJustEnabled = !realm.AbusePreventionEnabled && form.AbusePreventionEnabled
+
realm.AbusePreventionEnabled = form.AbusePreventionEnabled
realm.AbusePreventionLimitFactor = form.AbusePreventionLimitFactor
}
+ // If abuse prevention was just enabled, create the initial bucket so
+ // enforcement works. We do this before actually saving the configuration on
+ // the realm to avoid a race where someone is issuing a code where abuse
+ // prevention has been enabled, but the quota has not been set. In that
+ // case, the quota would be the "default" quota for the limiter, which is
+ // not ideal or correct.
+ //
+ // Even if saving the realm fails, there's no harm in doing this early. It's
+ // an idempotent operation that TTLs out after a week anyway.
+ if abusePreventionJustEnabled {
+ dig, err := digest.HMACUint(realm.ID, c.config.RateLimit.HMACKey)
+ if err != nil {
+ controller.InternalError(w, r, c.h, err)
+ return
+ }
+ key := fmt.Sprintf("realm:quota:%s", dig)
+ limit := uint64(realm.AbusePreventionEffectiveLimit())
+ ttl := 7 * 24 * time.Hour
+ if err := c.limiter.Set(ctx, key, limit, ttl); err != nil {
+ controller.InternalError(w, r, c.h, err)
+ return
+ }
+ }
+
// Save realm
if err := c.db.SaveRealm(realm, currentUser); err != nil {
flash.Error("Failed to update realm: %v", err)