Eric Bower
·
27 Oct 24
tunnel.go
1package pgs
2
3import (
4 "net/http"
5 "strings"
6
7 "github.com/charmbracelet/ssh"
8 "github.com/picosh/pico/db"
9 "github.com/picosh/pico/shared"
10 "github.com/picosh/utils"
11)
12
13type TunnelWebRouter struct {
14 *WebRouter
15}
16
17func (web *TunnelWebRouter) Perm(proj *db.Project) bool {
18 return true
19}
20
21type CtxHttpBridge = func(ssh.Context) http.Handler
22
23func getInfoFromUser(user string) (string, string) {
24 if strings.Contains(user, "__") {
25 results := strings.SplitN(user, "__", 2)
26 return results[0], results[1]
27 }
28
29 return "", user
30}
31
32func createHttpHandler(apiConfig *shared.ApiConfig) CtxHttpBridge {
33 return func(ctx ssh.Context) http.Handler {
34 dbh := apiConfig.Dbpool
35 logger := apiConfig.Cfg.Logger
36 asUser, subdomain := getInfoFromUser(ctx.User())
37 log := logger.With(
38 "subdomain", subdomain,
39 "impersonating", asUser,
40 )
41
42 pubkey, err := shared.GetPublicKey(ctx)
43 if err != nil {
44 log.Error(err.Error(), "subdomain", subdomain)
45 return http.HandlerFunc(shared.UnauthorizedHandler)
46 }
47
48 pubkeyStr := utils.KeyForKeyText(pubkey)
49
50 log = log.With(
51 "pubkey", pubkeyStr,
52 )
53
54 props, err := getProjectFromSubdomain(subdomain)
55 if err != nil {
56 log.Error(err.Error())
57 return http.HandlerFunc(shared.UnauthorizedHandler)
58 }
59
60 owner, err := dbh.FindUserForName(props.Username)
61 if err != nil {
62 log.Error(err.Error())
63 return http.HandlerFunc(shared.UnauthorizedHandler)
64 }
65 log = log.With(
66 "owner", owner.Name,
67 )
68
69 project, err := dbh.FindProjectByName(owner.ID, props.ProjectName)
70 if err != nil {
71 log.Error(err.Error())
72 return http.HandlerFunc(shared.UnauthorizedHandler)
73 }
74
75 requester, _ := dbh.FindUserForKey("", pubkeyStr)
76 if requester != nil {
77 log = log.With(
78 "requester", requester.Name,
79 )
80 }
81
82 // impersonation logic
83 if asUser != "" {
84 isAdmin := dbh.HasFeatureForUser(requester.ID, "admin")
85 if !isAdmin {
86 log.Error("impersonation attempt failed")
87 return http.HandlerFunc(shared.UnauthorizedHandler)
88 }
89 requester, _ = dbh.FindUserForName(asUser)
90 }
91
92 shared.SetUser(ctx, requester)
93
94 if !HasProjectAccess(project, owner, requester, pubkey) {
95 log.Error("no access")
96 return http.HandlerFunc(shared.UnauthorizedHandler)
97 }
98
99 log.Info("user has access to site")
100
101 /* routes := []shared.Route{
102 // special API endpoint for tunnel users accessing site
103 shared.NewCorsRoute("GET", "/api/current_user", func(w http.ResponseWriter, r *http.Request) {
104 w.Header().Set("Content-Type", "application/json")
105 user, err := shared.GetUser(ctx)
106 if err != nil {
107 logger.Error("could not find user", "err", err.Error())
108 shared.JSONError(w, err.Error(), http.StatusNotFound)
109 return
110 }
111 pico := shared.NewUserApi(user, pubkey)
112 err = json.NewEncoder(w).Encode(pico)
113 if err != nil {
114 log.Error(err.Error())
115 }
116 }),
117 } */
118
119 routes := NewWebRouter(
120 apiConfig.Cfg,
121 logger,
122 apiConfig.Dbpool,
123 apiConfig.Storage,
124 apiConfig.AnalyticsQueue,
125 )
126 tunnelRouter := TunnelWebRouter{routes}
127 router := http.NewServeMux()
128 router.HandleFunc("GET /{fname}/{options}...", tunnelRouter.ImageRequest)
129 router.HandleFunc("GET /{fname}", tunnelRouter.AssetRequest)
130 router.HandleFunc("GET /{$}", tunnelRouter.AssetRequest)
131
132 /* subdomainRoutes := createSubdomainRoutes(allowPerm)
133 routes = append(routes, subdomainRoutes...)
134 finctx := apiConfig.CreateCtx(context.Background(), subdomain)
135 finctx = context.WithValue(finctx, shared.CtxSshKey{}, ctx)
136 httpHandler := shared.CreateServeBasic(routes, finctx)
137 httpRouter := http.HandlerFunc(httpHandler) */
138 return router
139 }
140}