tinyrelayfile
tinyrelay/tinyclient/private.go/

tinyclient/private.go raw
1package tinyclient
2
3import (
4 "context"
5 "errors"
6 "net/http"
7 "strings"
8
9 "github.com/FelineStateMachine/tinyrelay/internal/policy"
10)
11
12// PrivateReader is implemented by the daemon so web pages use the same
13// membership check as websocket and HTTP data paths. It is optional to keep
14// webui usable with small embedders; those embedders get owner-only fallback.
15type PrivateReader interface {
16 ReadAllowed(context.Context, string) error
17}
18
19func privatePolicyEnabled(p policy.Policy) bool { return p.Features.Grasp08 }
20
21func privateShellPath(path string) bool {
22 if path == "/" || path == "/signin" || path == "/account" || path == "/manage/connect" {
23 return true
24 }
25 if path == "/manage/rpc" || path == "/manage/jobs/status" {
26 return false
27 }
28 return strings.HasPrefix(path, "/manage/") && !strings.HasSuffix(path, ".json")
29}
30
31func privateProtectedPath(path string) bool {
32 if privateShellPath(path) || path == "/webmcp.js" || path == "/signer.js" || path == "/fixi.js" || path == "/qr.svg" || path == "/sw.js" || path == "/manifest.webmanifest" || path == "/icon.svg" || path == "/icon-mono.svg" {
33 return false
34 }
35 return true
36}
37
38func (a *App) privateReadAllowed(request *http.Request, actor string) error {
39 if actor == "" {
40 return errors.New("auth-required: private relay membership required")
41 }
42 if reader, ok := a.backend.(PrivateReader); ok {
43 return reader.ReadAllowed(request.Context(), actor)
44 }
45 if actor != a.backend.Policy().Owner {
46 return errors.New("restricted: private relay membership required")
47 }
48 return nil
49}
50
51func (a *App) denyPrivateEndpoint(writer http.ResponseWriter, request *http.Request) bool {
52 if !privatePolicyEnabled(a.backend.Policy()) || !privateProtectedPath(request.URL.Path) {
53 return false
54 }
55 actor, err := a.resolveActor(request)
56 if err != nil || actor == "" {
57 writer.Header().Set("Cache-Control", "private, no-store")
58 http.Error(writer, "authentication required", http.StatusUnauthorized)
59 return true
60 }
61 if err := a.privateReadAllowed(request, actor); err != nil {
62 writer.Header().Set("Cache-Control", "private, no-store")
63 http.Error(writer, err.Error(), http.StatusForbidden)
64 return true
65 }
66 return false
67}
68
69func (a *App) privatePageData(data *PageData, request *http.Request, actor string) {
70 if !privatePolicyEnabled(data.Policy) || !privateShellPath(request.URL.Path) {
71 return
72 }
73 if err := a.privateReadAllowed(request, actor); err == nil {
74 return
75 }
76 data.Private = true
77 data.Title = "Private relay"
78 data.Slug = "private relay"
79 data.URL = ""
80 data.Identity = ""
81 data.Policy = policy.Policy{}
82 data.Actor = ""
83 data.Owner = false
84 data.Event = nil
85 data.Feed = nil
86 data.Methods = nil
87}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs