+1
knotserver/config/config.go
+1
knotserver/config/config.go
···
21
21
DBPath string `env:"DB_PATH, default=knotserver.db"`
22
22
Hostname string `env:"HOSTNAME, required"`
23
23
JetstreamEndpoint string `env:"JETSTREAM_ENDPOINT, default=wss://jetstream1.us-west.bsky.network/subscribe"`
24
+
Owner string `env:"OWNER, required"`
24
25
LogDids bool `env:"LOG_DIDS, default=true"`
25
26
26
27
// This disables signature verification so use with caution.
+50
-21
knotserver/handler.go
+50
-21
knotserver/handler.go
···
27
27
l *slog.Logger
28
28
n *notifier.Notifier
29
29
resolver *idresolver.Resolver
30
-
31
-
// init is a channel that is closed when the knot has been initailized
32
-
// i.e. when the first user (knot owner) has been added.
33
-
init chan struct{}
34
-
knotInitialized bool
35
30
}
36
31
37
32
func Setup(ctx context.Context, c *config.Config, db *db.DB, e *rbac.Enforcer, jc *jetstream.JetstreamClient, l *slog.Logger, n *notifier.Notifier) (http.Handler, error) {
···
45
40
jc: jc,
46
41
n: n,
47
42
resolver: idresolver.DefaultResolver(),
48
-
init: make(chan struct{}),
49
43
}
50
44
51
45
err := e.AddKnot(rbac.ThisServer)
···
53
47
return nil, fmt.Errorf("failed to setup enforcer: %w", err)
54
48
}
55
49
50
+
err = h.configureOwner()
51
+
if err != nil {
52
+
return nil, err
53
+
}
54
+
h.l.Info("owner set", "did", h.c.Server.Owner)
55
+
56
56
err = h.jc.StartJetstream(ctx, h.processMessages)
57
57
if err != nil {
58
58
return nil, fmt.Errorf("failed to start jetstream: %w", err)
59
59
}
60
60
61
-
// Check if the knot knows about any Dids;
62
-
// if it does, it is already initialized and we can repopulate the
63
-
// Jetstream subscriptions.
64
-
dids, err := db.GetAllDids()
61
+
h.jc.AddDid(h.c.Server.Owner)
62
+
63
+
// check if the knot knows about any dids
64
+
dids, err := h.db.GetAllDids()
65
65
if err != nil {
66
-
return nil, fmt.Errorf("failed to get all Dids: %w", err)
66
+
return nil, fmt.Errorf("failed to get all dids: %w", err)
67
67
}
68
-
69
-
if len(dids) > 0 {
70
-
h.knotInitialized = true
71
-
close(h.init)
72
-
for _, d := range dids {
73
-
h.jc.AddDid(d)
74
-
}
68
+
for _, d := range dids {
69
+
jc.AddDid(d)
75
70
}
76
71
77
72
r.Get("/", h.Index)
78
73
r.Get("/capabilities", h.Capabilities)
79
74
r.Get("/version", h.Version)
75
+
r.Get("/owner", func(w http.ResponseWriter, r *http.Request) {
76
+
w.Write([]byte(h.c.Server.Owner))
77
+
})
80
78
r.Route("/{did}", func(r chi.Router) {
81
79
// Repo routes
82
80
r.Route("/{name}", func(r chi.Router) {
···
154
152
// Socket that streams git oplogs
155
153
r.Get("/events", h.Events)
156
154
157
-
// Initialize the knot with an owner and public key.
158
-
r.With(h.VerifySignature).Post("/init", h.Init)
159
-
160
155
// Health check. Used for two-way verification with appview.
161
156
r.With(h.VerifySignature).Get("/health", h.Health)
162
157
···
211
206
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
212
207
fmt.Fprintf(w, "knotserver/%s", version)
213
208
}
209
+
210
+
func (h *Handle) configureOwner() error {
211
+
cfgOwner := h.c.Server.Owner
212
+
213
+
rbacDomain := "thisserver"
214
+
215
+
existing, err := h.e.GetKnotUsersByRole("server:owner", rbacDomain)
216
+
if err != nil {
217
+
return err
218
+
}
219
+
220
+
switch len(existing) {
221
+
case 0:
222
+
// no owner configured, continue
223
+
case 1:
224
+
// find existing owner
225
+
existingOwner := existing[0]
226
+
227
+
// no ownership change, this is okay
228
+
if existingOwner == h.c.Server.Owner {
229
+
break
230
+
}
231
+
232
+
// remove existing owner
233
+
err = h.e.RemoveKnotOwner(rbacDomain, existingOwner)
234
+
if err != nil {
235
+
return nil
236
+
}
237
+
default:
238
+
return fmt.Errorf("more than one owner in DB, try deleting %q and starting over", h.c.Server.DBPath)
239
+
}
240
+
241
+
return h.e.AddKnotOwner(rbacDomain, cfgOwner)
242
+
}
-54
knotserver/routes.go
-54
knotserver/routes.go
···
3
3
import (
4
4
"compress/gzip"
5
5
"context"
6
-
"crypto/hmac"
7
6
"crypto/sha256"
8
-
"encoding/hex"
9
7
"encoding/json"
10
8
"errors"
11
9
"fmt"
···
1201
1199
l.Error("setting default branch", "error", err.Error())
1202
1200
return
1203
1201
}
1204
-
1205
-
w.WriteHeader(http.StatusNoContent)
1206
-
}
1207
-
1208
-
func (h *Handle) Init(w http.ResponseWriter, r *http.Request) {
1209
-
l := h.l.With("handler", "Init")
1210
-
1211
-
if h.knotInitialized {
1212
-
writeError(w, "knot already initialized", http.StatusConflict)
1213
-
return
1214
-
}
1215
-
1216
-
data := struct {
1217
-
Did string `json:"did"`
1218
-
}{}
1219
-
1220
-
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
1221
-
l.Error("failed to decode request body", "error", err.Error())
1222
-
writeError(w, "invalid request body", http.StatusBadRequest)
1223
-
return
1224
-
}
1225
-
1226
-
if data.Did == "" {
1227
-
l.Error("empty DID in request", "did", data.Did)
1228
-
writeError(w, "did is empty", http.StatusBadRequest)
1229
-
return
1230
-
}
1231
-
1232
-
if err := h.db.AddDid(data.Did); err != nil {
1233
-
l.Error("failed to add DID", "error", err.Error())
1234
-
writeError(w, err.Error(), http.StatusInternalServerError)
1235
-
return
1236
-
}
1237
-
h.jc.AddDid(data.Did)
1238
-
1239
-
if err := h.e.AddKnotOwner(rbac.ThisServer, data.Did); err != nil {
1240
-
l.Error("adding owner", "error", err.Error())
1241
-
writeError(w, err.Error(), http.StatusInternalServerError)
1242
-
return
1243
-
}
1244
-
1245
-
if err := h.fetchAndAddKeys(r.Context(), data.Did); err != nil {
1246
-
l.Error("fetching and adding keys", "error", err.Error())
1247
-
writeError(w, err.Error(), http.StatusInternalServerError)
1248
-
return
1249
-
}
1250
-
1251
-
close(h.init)
1252
-
1253
-
mac := hmac.New(sha256.New, []byte(h.c.Server.Secret))
1254
-
mac.Write([]byte("ok"))
1255
-
w.Header().Add("X-Signature", hex.EncodeToString(mac.Sum(nil)))
1256
1202
1257
1203
w.WriteHeader(http.StatusNoContent)
1258
1204
}