~james/core

core/spindle/db/allowlist.go -rw-r--r-- 2.4 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 16 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package db

import (
	"database/sql"
	"errors"

	"github.com/bluesky-social/indigo/atproto/syntax"
	"tangled.org/core/spindle/config"
)

func AddToAllowlist(q DBTX, did syntax.DID) error {
	_, err := q.Exec(`insert or ignore into instance_allowlist (did) values (?)`, did)
	return err
}

func RemoveFromAllowlist(q DBTX, did syntax.DID) error {
	_, err := q.Exec(`delete from instance_allowlist where did = ?`, did)
	return err
}

func AllowlistContains(q DBTX, did syntax.DID) (bool, error) {
	var one int
	err := q.QueryRow(`select 1 from instance_allowlist where did = ?`, did).Scan(&one)
	switch {
	case err == sql.ErrNoRows:
		return false, nil
	case err != nil:
		return false, err
	default:
		return true, nil
	}
}

func ListAllowlist(q DBTX) ([]syntax.DID, error) {
	rows, err := q.Query(`select did from instance_allowlist order by did asc`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	dids := []syntax.DID{}
	for rows.Next() {
		var did string
		if err := rows.Scan(&did); err != nil {
			return nil, err
		}
		parsed, err := syntax.ParseDID(did)
		if err != nil {
			continue
		}
		dids = append(dids, parsed)
	}
	return dids, rows.Err()
}

// a ban beats everything, admins are served even when admission is closed
func Enrolls(q DBTX, srv *config.Server, did syntax.DID) (bool, error) {
	var banned, listed bool
	err := q.QueryRow(`
		select
			exists(select 1 from bans where subject_did = ?),
			exists(select 1 from instance_allowlist where did = ?)`,
		did, did,
	).Scan(&banned, &listed)
	if err != nil {
		return false, err
	}
	return !banned && (srv.OpenAdmission() || srv.IsAdmin(did.String()) || listed), nil
}

// a ban beats paused, and paused only means admission is closed
type RepoGate struct {
	Served bool
	Banned bool
	Paused bool
}

// keep this in step with Enrolls, they answer the same question
func (d *DB) Gate(srv *config.Server, repoDid syntax.DID) (RepoGate, error) {
	var owner string
	var banned, listed bool
	err := d.QueryRow(`
		select r.owner,
			exists(select 1 from bans where subject_did in (r.repo_did, r.owner)),
			exists(select 1 from instance_allowlist where did = r.owner)
		from repos r where r.repo_did = ? limit 1`,
		repoDid.String(),
	).Scan(&owner, &banned, &listed)
	if errors.Is(err, sql.ErrNoRows) {
		return RepoGate{}, nil
	}
	if err != nil {
		return RepoGate{}, err
	}
	enrolled := srv.OpenAdmission() || srv.IsAdmin(owner) || listed
	return RepoGate{Served: true, Banned: banned, Paused: !banned && !enrolled}, nil
}