From 3f80f677f56d5b4873cfd824a701a94aaaf19fd7 Mon Sep 17 00:00:00 2001 From: Conrad Hoffmann Date: Thu, 17 Mar 2022 10:15:45 +0100 Subject: [PATCH] email: handle failure to encrypt notifications Currently, failure to encrypt the notification email - e.g. due to an expired key - will make the entire request fail. Instead, fall back to sending an unencrypted email and let the request succeed (unless that fails also, which is unlikely). --- email/worker.go | 50 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/email/worker.go b/email/worker.go index 7b5fba02ce1be834d0e91a83c59d684c289da308..df96a4bec93cec242be45cd1005c7fe8e9655e0b 100644 --- a/email/worker.go +++ b/email/worker.go @@ -53,6 +53,30 @@ func Enqueue(ctx context.Context, msg *bytes.Buffer, rcpts []string) { ForContext(ctx).Enqueue(NewTask(msg, rcpts)) } +func prepareEncrypted(rcptKey *string, header mail.Header, + buf *bytes.Buffer, signed *openpgp.Entity) (io.WriteCloser, error) { + keyring, err := openpgp.ReadArmoredKeyRing(strings.NewReader(*rcptKey)) + if err != nil { + return nil, err + } + if len(keyring) != 1 { + return nil, errors.New("Expected user PGP key to contain one key") + } + rcptEntity := keyring[0] + + return pgpmail.Encrypt(buf, header.Header.Header, + []*openpgp.Entity{rcptEntity}, signed, nil) +} + +func prepareSigned(header mail.Header, buf *bytes.Buffer, + signed *openpgp.Entity) (io.WriteCloser, error) { + result, err := pgpmail.Sign(buf, header.Header.Header, signed, nil) + if err != nil { + return nil, err + } + return result, nil +} + // Updates an email with the standard SourceHut headers, signs and optionally // encrypts it, and then queues it for delivery. // @@ -137,29 +161,23 @@ func EnqueueStd(ctx context.Context, header mail.Header, ) if rcptKey != nil { - keyring, err = openpgp.ReadArmoredKeyRing(strings.NewReader(*rcptKey)) + cleartext, err = prepareEncrypted(rcptKey, header, &buf, entity) + } + // Fall back to unencrypted email if encryption did not work + // TODO should we add the error message to the email? + if rcptKey == nil || err != nil { if err != nil { - log.Fatal(err) - } - if len(keyring) != 1 { - return errors.New("Expected user PGP key to contain one key") + buf.Reset() + log.Printf("Encrypting mail to %s failed: %s", + strings.Join(rcpts, ", "), err.Error()) } - rcptEntity := keyring[0] - cleartext, err = pgpmail.Encrypt(&buf, header.Header.Header, - []*openpgp.Entity{rcptEntity}, entity, nil) - if err != nil { - log.Fatal(err) - } - defer cleartext.Close() - } else { - cleartext, err = pgpmail.Sign(&buf, header.Header.Header, - entity, nil) + cleartext, err = prepareSigned(header, &buf, entity) if err != nil { log.Fatal(err) } - defer cleartext.Close() } + defer cleartext.Close() var inlineHeader mail.Header inlineHeader.SetContentType("text/plain", nil)