api: make sendEmailNotification recipient explicit

Having sendEmailNotification take a full message as argument is very
powerful, but restricting it to only send mail to the currently
logged-in user will in fact not get us very far. Examples of mails that
should be sent (potentially encrypted) that are not triggered by a
logged-in user include the password reset email, billing emails
triggered by metasrht-daily, and possibly more in other services.

This commit extends the API to include the username of the desired
recipient. This will not add much overhead to the callers and more
importantly, still make sure to keep any PGP-related code in meta.sr.ht.
This commit is contained in:
Conrad Hoffmann 2022-11-07 13:21:44 +01:00 committed by Drew DeVault
parent b63e211398
commit 7dd83975ae
3 changed files with 34 additions and 8 deletions

View File

@ -115,7 +115,8 @@ to this email.
}
}
func sendEmailNotification(ctx context.Context, message string) error {
func sendEmailNotification(ctx context.Context,
username, userEmail, message string, pgpKey *string) error {
r := strings.NewReader(message)
mr, err := mail.CreateReader(r)
if err != nil {
@ -151,12 +152,11 @@ func sendEmailNotification(ctx context.Context, message string) error {
return fmt.Errorf("missing or malformed subject")
}
user := auth.ForContext(ctx)
header.SetAddressList("To", []*mail.Address{
&mail.Address{user.Username, user.Email},
&mail.Address{username, userEmail},
})
return email.EnqueueStd(ctx, header, p.Body, user.PGPKey)
return email.EnqueueStd(ctx, header, p.Body, pgpKey)
}
// Sends a security-related notice to the authorized user.

View File

@ -519,13 +519,13 @@ type Mutation {
clientSecret: String!, redirectUri: String): OAuthGrantRegistration @internal
"""
Sends a notification email to the currently logged-in user.
Sends a notification email to the given user.
The 'message' parameter must be a RFC 5322 compliant Internet message with
the special requirement that it must not contain any recipients (i.e. no
'To', 'Cc', or 'Bcc' header).
"""
sendEmailNotification(message: String!): Boolean! @internal
sendEmailNotification(username: String!, message: String!): Boolean! @anoninternal
"""
Deletes the authenticated user's account.

View File

@ -1214,8 +1214,34 @@ func (r *mutationResolver) IssueOAuthGrant(ctx context.Context, authorization st
}
// SendEmailNotification is the resolver for the sendEmailNotification field.
func (r *mutationResolver) SendEmailNotification(ctx context.Context, message string) (bool, error) {
err := sendEmailNotification(ctx, message)
func (r *mutationResolver) SendEmailNotification(ctx context.Context, username string, message string) (bool, error) {
user, err := loaders.ForContext(ctx).UsersByName.Load(username)
if err != nil {
return false, err
}
if user == nil {
return false, fmt.Errorf("Email notification request to unknown user: %s", user)
}
var key *string
if user.PGPKeyID != nil {
if err := database.WithTx(ctx, &sql.TxOptions{
Isolation: 0,
ReadOnly: true,
}, func(tx *sql.Tx) error {
row := tx.QueryRowContext(ctx, `
SELECT key
FROM "pgpkey" WHERE id = $1;
`, *user.PGPKeyID)
if err := row.Scan(&key); err != nil {
return err
}
return nil
}); err != nil {
return false, err
}
}
err = sendEmailNotification(ctx, user.Username, user.Email, message, key)
return err == nil, err
}