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