tenseleyflow/shithub / 3168964

Browse files

auth/audit + auth/email: GPG key add/delete plumbing

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
316896479f1bebfc9bb6d271d1aee7ba25e92721
Parents
da0e8f5
Tree
fada69a

2 changed files

StatusFile+-
M internal/auth/audit/audit.go 2 0
M internal/auth/email/messages.go 54 0
internal/auth/audit/audit.gomodified
@@ -42,6 +42,8 @@ const (
4242
 	ActionAccountSuspended      Action = "account_suspended"
4343
 	ActionSSHKeyAdded           Action = "ssh_key_added"
4444
 	ActionSSHKeyDeleted         Action = "ssh_key_deleted"
45
+	ActionGPGKeyAdded           Action = "gpg_key_added"
46
+	ActionGPGKeyDeleted         Action = "gpg_key_deleted"
4547
 	ActionPATCreated            Action = "pat_created"
4648
 	ActionPATRevoked            Action = "pat_revoked"
4749
 	ActionUsernameChanged       Action = "username_changed"
internal/auth/email/messages.gomodified
@@ -278,6 +278,60 @@ If this wasn't you, sign in immediately, delete the key from
278278
 	}, nil
279279
 }
280280
 
281
+// GPGKeyAddedMessage builds the new-GPG-key notification email. Sent
282
+// on successful key add — name/fingerprint/IP help the user spot a
283
+// compromise. Mirrors SSHKeyAddedMessage; the only visible difference
284
+// in the body is the wording ("GPG key" vs "SSH key") and the
285
+// settings link target.
286
+//
287
+// The display "name" comes from the user-supplied title field; when
288
+// the user omits it (gh allows blank), we fall back to a generic
289
+// "(no title)" label so the email body stays readable.
290
+func GPGKeyAddedMessage(b Branding, to, username, name, fingerprint, ip string) (Message, error) {
291
+	if strings.TrimSpace(name) == "" {
292
+		name = "(no title)"
293
+	}
294
+	const text = `Hi {{.Username}},
295
+
296
+A new GPG key was added to your {{.SiteName}} account.
297
+
298
+  Name: {{.Name}}
299
+  Fingerprint: {{.Fingerprint}}
300
+{{if .IP}}  IP: {{.IP}}
301
+{{end}}
302
+If this wasn't you, sign in immediately, delete the key from
303
+{{.BaseURL}}/settings/keys, and reset your password.`
304
+
305
+	const html = `<p>Hi <strong>{{.Username}}</strong>,</p>
306
+<p>A new GPG key was added to your {{.SiteName}} account.</p>
307
+<ul>
308
+  <li><strong>Name:</strong> {{.Name}}</li>
309
+  <li><strong>Fingerprint:</strong> <code>{{.Fingerprint}}</code></li>
310
+  {{if .IP}}<li><strong>IP:</strong> {{.IP}}</li>{{end}}
311
+</ul>
312
+<p>If this wasn't you, sign in immediately, delete the key from
313
+<a href="{{.BaseURL}}/settings/keys">your SSH and GPG keys settings</a>, and reset your password.</p>`
314
+
315
+	data := struct{ SiteName, BaseURL, Username, Name, Fingerprint, IP string }{
316
+		b.SiteName, b.BaseURL, username, name, fingerprint, ip,
317
+	}
318
+	txt, err := renderText(textTemplate.Must(textTemplate.New("gpg_added.txt").Parse(text)), data)
319
+	if err != nil {
320
+		return Message{}, err
321
+	}
322
+	htmlBody, err := renderHTML(template.Must(template.New("gpg_added.html").Parse(html)), data)
323
+	if err != nil {
324
+		return Message{}, err
325
+	}
326
+	return Message{
327
+		From:    b.From,
328
+		To:      to,
329
+		Subject: fmt.Sprintf("New GPG key added to your %s account", b.SiteName),
330
+		Text:    txt,
331
+		HTML:    htmlBody,
332
+	}, nil
333
+}
334
+
281335
 // NoticeMessage builds a 2FA / security state-change notice for kind. The
282336
 // kind names match audit-log Action values where applicable.
283337
 func NoticeMessage(b Branding, to, username, kind string) (Message, error) {