internal/store/revoke.go

f8b976a97290a20d552056a999511f5d27d8e8ec
gitbay/internal/store/revoke.go history · blame · raw

62 lines · 1629 bytes

 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}