Monorepo for Tangled
tangled.org
1package xrpc
2
3import (
4 "crypto/subtle"
5 "encoding/json"
6 "errors"
7 "net/http"
8
9 "github.com/bluesky-social/indigo/atproto/syntax"
10 "github.com/go-chi/chi/v5"
11 "tangled.org/core/api/tangled"
12 xrpcerr "tangled.org/core/xrpc/errors"
13)
14
15const maxAdminBodyBytes = 4 << 10
16
17func (x *Xrpc) AdminRouter() http.Handler {
18 r := chi.NewRouter()
19 r.Use(x.VerifyAdminSecret)
20 r.Post("/addMember", x.AddMemberAdmin)
21 return r
22}
23
24func (x *Xrpc) VerifyAdminSecret(next http.Handler) http.Handler {
25 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
26 secret := x.Config.Server.AdminSecret
27 user, pass, ok := r.BasicAuth()
28 valid := secret != "" &&
29 ok &&
30 user == "admin" &&
31 subtle.ConstantTimeCompare([]byte(pass), []byte(secret)) == 1
32 if !valid {
33 writeError(w, xrpcerr.AuthError(errors.New("invalid admin credentials")), http.StatusUnauthorized)
34 return
35 }
36 next.ServeHTTP(w, r)
37 })
38}
39
40func (x *Xrpc) AddMemberAdmin(w http.ResponseWriter, r *http.Request) {
41 l := x.Logger.With("handler", "AddMemberAdmin")
42 fail := func(e xrpcerr.XrpcError, status int) {
43 l.Error("failed", "kind", e.Tag, "error", e.Message)
44 writeError(w, e, status)
45 }
46
47 var data tangled.KnotAddMember_Input
48 if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, maxAdminBodyBytes)).Decode(&data); err != nil {
49 fail(xrpcerr.GenericError(err), http.StatusBadRequest)
50 return
51 }
52
53 subject, err := syntax.ParseDID(data.Subject)
54 if err != nil {
55 fail(xrpcerr.GenericError(err), http.StatusBadRequest)
56 return
57 }
58
59 owner, err := syntax.ParseDID(x.Config.Server.Owner)
60 if err != nil {
61 fail(xrpcerr.GenericError(err), http.StatusInternalServerError)
62 return
63 }
64
65 status, xerr := x.addMemberToKnot(r.Context(), l, owner, subject)
66 if xerr != nil {
67 fail(*xerr, status)
68 return
69 }
70 w.WriteHeader(status)
71}