panel,mail: fail closed on the rate-limit race, session create and app delete
test / test (push) Has been cancelled
test / test (push) Has been cancelled
The level-2 limiter counted stored plus in-flight messages and reserved its own slot in two critical sections, so SMTP sessions that overlapped could each take the last free slot; tryAdmit now does both under one lock. A session that cannot be written no longer yields a cookie the browser would carry while every request bounced to /login. Deleting an application clears its SASL account before its registry row, matching domain delete, so a saslpasswd2 failure leaves a retryable application rather than an account that still authenticates. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+11
-1
@@ -166,14 +166,24 @@ func (s *Service) RegeneratePassword(id int64) (string, error) {
|
||||
// Delete removes an application: its SASL account, its registry row (and address
|
||||
// rows via cascade) and its sender-map bindings, then reloads Postfix (spec
|
||||
// 7.2.8). The domain and other applications are untouched.
|
||||
//
|
||||
// The order matches domain deletion: the SASL account goes first, while the
|
||||
// login is still in the registry. Dropping the row first would, on a
|
||||
// saslpasswd2 failure, leave an account that can still authenticate to Postfix
|
||||
// but that the panel no longer knows about — an orphan no operator can see or
|
||||
// remove. Failing before the row is deleted is recoverable: the application is
|
||||
// still listed and the delete can be retried.
|
||||
func (s *Service) Delete(id int64) error {
|
||||
a, err := s.store.DeleteApplication(id)
|
||||
a, err := s.store.GetApplication(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.sasl.Delete(a.Login); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.store.DeleteApplication(id); err != nil {
|
||||
return err
|
||||
}
|
||||
// Drop the application's level-2 limit, if any (guide § Rate limiting);
|
||||
// rate_limits has no cascade of its own.
|
||||
if err := s.store.DeleteRateLimit(store.RateLimitScopeApp, id); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user