repos / pico

pico services - prose.sh, pastes.sh, imgs.sh, feeds.sh, pgs.sh
git clone https://github.com/picosh/pico.git

pico / pgs
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}