internal/store/revoke.go
62 lines · 1629 bytes
4 symbols in this file
1package store
2
3import (
4 "slices"
5 "strings"
6 "time"
7)
8
9// Revoked names SSH keys that stopped being valid: by id, or every key
10// of an account. The SSH listener closes the connections they opened.
11type Revoked struct {
12 KeyIDs []int64
13 UserID int64 // every key of this account; 0 for none
14}
15
16// OnRevoke registers f to run after each revocation this process
17// commits. Revocations committed by another process (gitbayd admin on
18// the host) are not announced; the listener's sweep finds those.
19func (s *Store) OnRevoke(f func(Revoked)) {
20 s.revokeMu.Lock()
21 defer s.revokeMu.Unlock()
22 s.onRevoke = append(s.onRevoke, f)
23}
24
25// announce runs the subscribers. Call it after the commit, outside any
26// transaction.
27func (s *Store) announce(r Revoked) {
28 s.revokeMu.Lock()
29 fs := slices.Clone(s.onRevoke)
30 s.revokeMu.Unlock()
31 for _, f := range fs {
32 f(r)
33 }
34}
35
36// LiveSSHKeys reports which of ids still name a registered, unexpired
37// key on an account that is not disabled.
38func (s *Store) LiveSSHKeys(ids []int64) (map[int64]bool, error) {
39 live := map[int64]bool{}
40 if len(ids) == 0 {
41 return live, nil
42 }
43 args := []any{fmtTime(time.Now())}
44 for _, id := range ids {
45 args = append(args, id)
46 }
47 rows, err := s.DB.Query(`SELECT k.id FROM ssh_keys k JOIN users u ON u.id = k.user_id
48 WHERE u.disabled = 0 AND (k.expires_at IS NULL OR k.expires_at > ?)
49 AND k.id IN (?`+strings.Repeat(", ?", len(ids)-1)+`)`, args...)
50 if err != nil {
51 return nil, err
52 }
53 defer rows.Close()
54 for rows.Next() {
55 var id int64
56 if err := rows.Scan(&id); err != nil {
57 return nil, err
58 }
59 live[id] = true
60 }
61 return live, rows.Err()
62}