- commit
- f650e5e
- parent
- 74f0af0
- author
- Eric Bower
- date
- 2024-03-13 16:22:57 +0000 UTC
feat(pgs): impersonation working
10 files changed,
+78,
-58
+2,
-2
1@@ -64,12 +64,12 @@ func StartApiServer() {
2
3 mainRoutes := createMainRoutes(staticRoutes)
4
5- httpCtx := &shared.HttpCtx{
6+ apiConfig := &shared.ApiConfig{
7 Cfg: cfg,
8 Dbpool: db,
9 Storage: st,
10 }
11- handler := shared.CreateServe(mainRoutes, []shared.Route{}, httpCtx)
12+ handler := shared.CreateServe(mainRoutes, []shared.Route{}, apiConfig)
13 router := http.HandlerFunc(handler)
14
15 portStr := fmt.Sprintf(":%s", cfg.Port)
+2,
-2
1@@ -322,12 +322,12 @@ func StartApiServer() {
2 mainRoutes := createMainRoutes(staticRoutes)
3 subdomainRoutes := createSubdomainRoutes(staticRoutes)
4
5- httpCtx := &shared.HttpCtx{
6+ apiConfig := &shared.ApiConfig{
7 Cfg: cfg,
8 Dbpool: db,
9 Storage: st,
10 }
11- handler := shared.CreateServe(mainRoutes, subdomainRoutes, httpCtx)
12+ handler := shared.CreateServe(mainRoutes, subdomainRoutes, apiConfig)
13 router := http.HandlerFunc(handler)
14
15 portStr := fmt.Sprintf(":%s", cfg.Port)
+2,
-2
1@@ -379,12 +379,12 @@ func StartApiServer() {
2 mainRoutes := createMainRoutes(staticRoutes)
3 subdomainRoutes := createSubdomainRoutes(staticRoutes)
4
5- httpCtx := &shared.HttpCtx{
6+ apiConfig := &shared.ApiConfig{
7 Cfg: cfg,
8 Dbpool: db,
9 Storage: st,
10 }
11- handler := shared.CreateServe(mainRoutes, subdomainRoutes, httpCtx)
12+ handler := shared.CreateServe(mainRoutes, subdomainRoutes, apiConfig)
13 router := http.HandlerFunc(handler)
14
15 portStr := fmt.Sprintf(":%s", cfg.Port)
+2,
-2
1@@ -441,12 +441,12 @@ func StartApiServer() {
2 return
3 }
4
5- httpCtx := &shared.HttpCtx{
6+ apiConfig := &shared.ApiConfig{
7 Cfg: cfg,
8 Dbpool: db,
9 Storage: st,
10 }
11- handler := shared.CreateServe(mainRoutes, createSubdomainRoutes(publicPerm), httpCtx)
12+ handler := shared.CreateServe(mainRoutes, createSubdomainRoutes(publicPerm), apiConfig)
13 router := http.HandlerFunc(handler)
14
15 portStr := fmt.Sprintf(":%s", cfg.Port)
+2,
-2
1@@ -86,7 +86,7 @@ func StartSshServer() {
2 st,
3 )
4
5- httpCtx := &shared.HttpCtx{
6+ apiConfig := &shared.ApiConfig{
7 Cfg: cfg,
8 Dbpool: dbh,
9 Storage: st,
10@@ -94,7 +94,7 @@ func StartSshServer() {
11
12 webTunnel := &ptun.WebTunnelHandler{
13 Logger: logger,
14- HttpHandler: createHttpHandler(httpCtx),
15+ HttpHandler: createHttpHandler(apiConfig),
16 }
17
18 s, err := wish.NewServer(
+8,
-6
1@@ -19,17 +19,17 @@ type CtxHttpBridge = func(ssh.Context) http.Handler
2
3 func getInfoFromUser(user string) (string, string) {
4 if strings.Contains(user, "__") {
5- results := strings.SplitN(user, "__", 1)
6+ results := strings.SplitN(user, "__", 2)
7 return results[0], results[1]
8 }
9
10 return "", user
11 }
12
13-func createHttpHandler(httpCtx *shared.HttpCtx) CtxHttpBridge {
14+func createHttpHandler(apiConfig *shared.ApiConfig) CtxHttpBridge {
15 return func(ctx ssh.Context) http.Handler {
16- dbh := httpCtx.Dbpool
17- logger := httpCtx.Cfg.Logger
18+ dbh := apiConfig.Dbpool
19+ logger := apiConfig.Cfg.Logger
20 asUser, subdomain := getInfoFromUser(ctx.User())
21 log := logger.With(
22 "subdomain", subdomain,
23@@ -88,6 +88,8 @@ func createHttpHandler(httpCtx *shared.HttpCtx) CtxHttpBridge {
24 requester, _ = dbh.FindUserForName(asUser)
25 }
26
27+ shared.SetUserCtx(ctx, requester)
28+
29 if !HasProjectAccess(project, owner, requester, pubkey) {
30 log.Error("no access")
31 return http.HandlerFunc(shared.UnauthorizedHandler)
32@@ -108,13 +110,13 @@ func createHttpHandler(httpCtx *shared.HttpCtx) CtxHttpBridge {
33 }
34
35 if subdomain == "pico-ui" || subdomain == "erock-ui" {
36- rts := ui.CreateRoutes(httpCtx, ctx)
37+ rts := ui.CreateRoutes(apiConfig, ctx)
38 routes = append(routes, rts...)
39 }
40
41 subdomainRoutes := createSubdomainRoutes(allowPerm)
42 routes = append(routes, subdomainRoutes...)
43- finctx := httpCtx.CreateCtx(ctx, subdomain)
44+ finctx := apiConfig.CreateCtx(ctx, subdomain)
45 httpHandler := shared.CreateServeBasic(routes, finctx)
46 httpRouter := http.HandlerFunc(httpHandler)
47 return httpRouter
+2,
-2
1@@ -920,12 +920,12 @@ func StartApiServer() {
2 mainRoutes := createMainRoutes(staticRoutes)
3 subdomainRoutes := createSubdomainRoutes(staticRoutes)
4
5- httpCtx := &shared.HttpCtx{
6+ apiConfig := &shared.ApiConfig{
7 Cfg: cfg,
8 Dbpool: db,
9 Storage: st,
10 }
11- handler := shared.CreateServe(mainRoutes, subdomainRoutes, httpCtx)
12+ handler := shared.CreateServe(mainRoutes, subdomainRoutes, apiConfig)
13 router := http.HandlerFunc(handler)
14
15 portStr := fmt.Sprintf(":%s", cfg.Port)
1@@ -55,13 +55,13 @@ func CreatePProfRoutes(routes []Route) []Route {
2 }
3
4 type ServeFn func(http.ResponseWriter, *http.Request)
5-type HttpCtx struct {
6+type ApiConfig struct {
7 Cfg *ConfigSite
8 Dbpool db.DB
9 Storage storage.StorageServe
10 }
11
12-func (hc *HttpCtx) CreateCtx(prevCtx context.Context, subdomain string) context.Context {
13+func (hc *ApiConfig) CreateCtx(prevCtx context.Context, subdomain string) context.Context {
14 loggerCtx := context.WithValue(prevCtx, ctxLoggerKey{}, hc.Cfg.Logger)
15 subdomainCtx := context.WithValue(loggerCtx, ctxSubdomainKey{}, subdomain)
16 dbCtx := context.WithValue(subdomainCtx, ctxDBKey{}, hc.Dbpool)
17@@ -129,10 +129,10 @@ func findRouteConfig(r *http.Request, routes []Route, subdomainRoutes []Route, c
18 return curRoutes, subdomain
19 }
20
21-func CreateServe(routes []Route, subdomainRoutes []Route, httpCtx *HttpCtx) ServeFn {
22+func CreateServe(routes []Route, subdomainRoutes []Route, apiConfig *ApiConfig) ServeFn {
23 return func(w http.ResponseWriter, r *http.Request) {
24- curRoutes, subdomain := findRouteConfig(r, routes, subdomainRoutes, httpCtx.Cfg)
25- ctx := httpCtx.CreateCtx(r.Context(), subdomain)
26+ curRoutes, subdomain := findRouteConfig(r, routes, subdomainRoutes, apiConfig.Cfg)
27+ ctx := apiConfig.CreateCtx(r.Context(), subdomain)
28 router := CreateServeBasic(curRoutes, ctx)
29 router(w, r)
30 }
1@@ -4,9 +4,11 @@ import (
2 "fmt"
3
4 "github.com/charmbracelet/ssh"
5+ "github.com/picosh/pico/db"
6 )
7
8 type ctxPublicKey struct{}
9+type ctxUserKey struct{}
10
11 func GetPublicKeyCtx(ctx ssh.Context) (ssh.PublicKey, error) {
12 pk, ok := ctx.Value(ctxPublicKey{}).(ssh.PublicKey)
13@@ -15,6 +17,19 @@ func GetPublicKeyCtx(ctx ssh.Context) (ssh.PublicKey, error) {
14 }
15 return pk, nil
16 }
17+
18 func SetPublicKeyCtx(ctx ssh.Context, pk ssh.PublicKey) {
19 ctx.SetValue(ctxPublicKey{}, pk)
20 }
21+
22+func GetUserCtx(ctx ssh.Context) (*db.User, error) {
23+ pk, ok := ctx.Value(ctxUserKey{}).(*db.User)
24+ if !ok {
25+ return nil, fmt.Errorf("user not set on `ssh.Context()` for connection")
26+ }
27+ return pk, nil
28+}
29+
30+func SetUserCtx(ctx ssh.Context, user *db.User) {
31+ ctx.SetValue(ctxUserKey{}, user)
32+}
+38,
-35
1@@ -24,8 +24,8 @@ func ensureUser(w http.ResponseWriter, user *db.User) bool {
2 return true
3 }
4
5-func registerUser(httpCtx *shared.HttpCtx, ctx ssh.Context, pubkey ssh.PublicKey, pubkeyStr string) http.HandlerFunc {
6- logger := httpCtx.Cfg.Logger
7+func registerUser(apiConfig *shared.ApiConfig, ctx ssh.Context, pubkey ssh.PublicKey, pubkeyStr string) http.HandlerFunc {
8+ logger := apiConfig.Cfg.Logger
9 return func(w http.ResponseWriter, r *http.Request) {
10 w.Header().Set("Content-Type", "application/json")
11 dbpool := shared.GetDB(r)
12@@ -53,8 +53,8 @@ type featuresPayload struct {
13 Features []*db.FeatureFlag `json:"features"`
14 }
15
16-func getFeatures(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.HandlerFunc {
17- logger := httpCtx.Cfg.Logger
18+func getFeatures(apiConfig *shared.ApiConfig, ctx ssh.Context, user *db.User) http.HandlerFunc {
19+ logger := apiConfig.Cfg.Logger
20 return func(w http.ResponseWriter, r *http.Request) {
21 w.Header().Set("Content-Type", "application/json")
22 if !ensureUser(w, user) {
23@@ -82,8 +82,8 @@ type tokenSecretPayload struct {
24 Secret string `json:"secret"`
25 }
26
27-func findOrCreateRssToken(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.HandlerFunc {
28- logger := httpCtx.Cfg.Logger
29+func findOrCreateRssToken(apiConfig *shared.ApiConfig, ctx ssh.Context, user *db.User) http.HandlerFunc {
30+ logger := apiConfig.Cfg.Logger
31 return func(w http.ResponseWriter, r *http.Request) {
32 w.Header().Set("Content-Type", "application/json")
33 if !ensureUser(w, user) {
34@@ -112,7 +112,7 @@ type pubkeysPayload struct {
35 Pubkeys []*db.PublicKey `json:"pubkeys"`
36 }
37
38-func getPublicKeys(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.HandlerFunc {
39+func getPublicKeys(httpCtx *shared.ApiConfig, ctx ssh.Context, user *db.User) http.HandlerFunc {
40 logger := httpCtx.Cfg.Logger
41 return func(w http.ResponseWriter, r *http.Request) {
42 w.Header().Set("Content-Type", "application/json")
43@@ -148,7 +148,7 @@ type createPubkeyPayload struct {
44 Name string `json:"name"`
45 }
46
47-func createPubkey(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.HandlerFunc {
48+func createPubkey(httpCtx *shared.ApiConfig, ctx ssh.Context, user *db.User) http.HandlerFunc {
49 logger := httpCtx.Cfg.Logger
50 return func(w http.ResponseWriter, r *http.Request) {
51 w.Header().Set("Content-Type", "application/json")
52@@ -173,7 +173,7 @@ func createPubkey(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.
53 }
54 }
55
56-func deletePubkey(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.HandlerFunc {
57+func deletePubkey(httpCtx *shared.ApiConfig, ctx ssh.Context, user *db.User) http.HandlerFunc {
58 logger := httpCtx.Cfg.Logger
59 return func(w http.ResponseWriter, r *http.Request) {
60 w.Header().Set("Content-Type", "application/json")
61@@ -197,7 +197,7 @@ type tokensPayload struct {
62 Tokens []*db.Token `json:"tokens"`
63 }
64
65-func getTokens(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.HandlerFunc {
66+func getTokens(httpCtx *shared.ApiConfig, ctx ssh.Context, user *db.User) http.HandlerFunc {
67 logger := httpCtx.Cfg.Logger
68 return func(w http.ResponseWriter, r *http.Request) {
69 w.Header().Set("Content-Type", "application/json")
70@@ -232,7 +232,7 @@ type createTokenResponsePayload struct {
71 Token *db.Token `json:"token"`
72 }
73
74-func createToken(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.HandlerFunc {
75+func createToken(httpCtx *shared.ApiConfig, ctx ssh.Context, user *db.User) http.HandlerFunc {
76 logger := httpCtx.Cfg.Logger
77 return func(w http.ResponseWriter, r *http.Request) {
78 w.Header().Set("Content-Type", "application/json")
79@@ -271,7 +271,7 @@ func createToken(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.H
80 }
81 }
82
83-func deleteToken(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.HandlerFunc {
84+func deleteToken(httpCtx *shared.ApiConfig, ctx ssh.Context, user *db.User) http.HandlerFunc {
85 logger := httpCtx.Cfg.Logger
86 return func(w http.ResponseWriter, r *http.Request) {
87 w.Header().Set("Content-Type", "application/json")
88@@ -295,7 +295,7 @@ type projectsPayload struct {
89 Projects []*db.Project `json:"projects"`
90 }
91
92-func getProjects(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.HandlerFunc {
93+func getProjects(httpCtx *shared.ApiConfig, ctx ssh.Context, user *db.User) http.HandlerFunc {
94 logger := httpCtx.Cfg.Logger
95 return func(w http.ResponseWriter, r *http.Request) {
96 w.Header().Set("Content-Type", "application/json")
97@@ -325,7 +325,7 @@ type postsPayload struct {
98 Posts []*db.Post `json:"posts"`
99 }
100
101-func getPosts(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User, space string) http.HandlerFunc {
102+func getPosts(httpCtx *shared.ApiConfig, ctx ssh.Context, user *db.User, space string) http.HandlerFunc {
103 logger := httpCtx.Cfg.Logger
104 return func(w http.ResponseWriter, r *http.Request) {
105 w.Header().Set("Content-Type", "application/json")
106@@ -362,9 +362,9 @@ type ProjectObject struct {
107 ModTime time.Time `json:"mod_time"`
108 }
109
110-func getProjectObjects(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User) http.HandlerFunc {
111- logger := httpCtx.Cfg.Logger
112- storage := httpCtx.Storage
113+func getProjectObjects(apiConfig *shared.ApiConfig, ctx ssh.Context, user *db.User) http.HandlerFunc {
114+ logger := apiConfig.Cfg.Logger
115+ storage := apiConfig.Storage
116 return func(w http.ResponseWriter, r *http.Request) {
117 w.Header().Set("Content-Type", "application/json")
118 if !ensureUser(w, user) {
119@@ -403,9 +403,8 @@ func getProjectObjects(httpCtx *shared.HttpCtx, ctx ssh.Context, user *db.User)
120 }
121 }
122
123-func CreateRoutes(httpCtx *shared.HttpCtx, ctx ssh.Context) []shared.Route {
124- logger := httpCtx.Cfg.Logger
125- dbpool := httpCtx.Dbpool
126+func CreateRoutes(apiConfig *shared.ApiConfig, ctx ssh.Context) []shared.Route {
127+ logger := apiConfig.Cfg.Logger
128 pubkey, err := shared.GetPublicKeyCtx(ctx)
129 if err != nil {
130 logger.Error("could not get pubkey from ctx", "err", err.Error())
131@@ -418,22 +417,26 @@ func CreateRoutes(httpCtx *shared.HttpCtx, ctx ssh.Context) []shared.Route {
132 return []shared.Route{}
133 }
134
135- user, _ := dbpool.FindUserForKey("", pubkeyStr)
136+ user, err := shared.GetUserCtx(ctx)
137+ if err != nil {
138+ logger.Error("could not convert key to text", "err", err.Error())
139+ return []shared.Route{}
140+ }
141
142 return []shared.Route{
143- shared.NewCorsRoute("POST", "/api/users", registerUser(httpCtx, ctx, pubkey, pubkeyStr)),
144- shared.NewCorsRoute("GET", "/api/features", getFeatures(httpCtx, ctx, user)),
145- shared.NewCorsRoute("PUT", "/api/rss-token", findOrCreateRssToken(httpCtx, ctx, user)),
146- shared.NewCorsRoute("GET", "/api/pubkeys", getPublicKeys(httpCtx, ctx, user)),
147- shared.NewCorsRoute("POST", "/api/pubkeys", createPubkey(httpCtx, ctx, user)),
148- shared.NewCorsRoute("DELETE", "/api/pubkeys/(.+)", deletePubkey(httpCtx, ctx, user)),
149- shared.NewCorsRoute("GET", "/api/tokens", getTokens(httpCtx, ctx, user)),
150- shared.NewCorsRoute("POST", "/api/tokens", createToken(httpCtx, ctx, user)),
151- shared.NewCorsRoute("DELETE", "/api/tokens/(.+)", deleteToken(httpCtx, ctx, user)),
152- shared.NewCorsRoute("GET", "/api/projects", getProjects(httpCtx, ctx, user)),
153- shared.NewCorsRoute("GET", "/api/projects/(.+)", getProjectObjects(httpCtx, ctx, user)),
154- shared.NewCorsRoute("GET", "/api/posts/prose", getPosts(httpCtx, ctx, user, "prose")),
155- shared.NewCorsRoute("GET", "/api/posts/pastes", getPosts(httpCtx, ctx, user, "pastes")),
156- shared.NewCorsRoute("GET", "/api/posts/feeds", getPosts(httpCtx, ctx, user, "feeds")),
157+ shared.NewCorsRoute("POST", "/api/users", registerUser(apiConfig, ctx, pubkey, pubkeyStr)),
158+ shared.NewCorsRoute("GET", "/api/features", getFeatures(apiConfig, ctx, user)),
159+ shared.NewCorsRoute("PUT", "/api/rss-token", findOrCreateRssToken(apiConfig, ctx, user)),
160+ shared.NewCorsRoute("GET", "/api/pubkeys", getPublicKeys(apiConfig, ctx, user)),
161+ shared.NewCorsRoute("POST", "/api/pubkeys", createPubkey(apiConfig, ctx, user)),
162+ shared.NewCorsRoute("DELETE", "/api/pubkeys/(.+)", deletePubkey(apiConfig, ctx, user)),
163+ shared.NewCorsRoute("GET", "/api/tokens", getTokens(apiConfig, ctx, user)),
164+ shared.NewCorsRoute("POST", "/api/tokens", createToken(apiConfig, ctx, user)),
165+ shared.NewCorsRoute("DELETE", "/api/tokens/(.+)", deleteToken(apiConfig, ctx, user)),
166+ shared.NewCorsRoute("GET", "/api/projects", getProjects(apiConfig, ctx, user)),
167+ shared.NewCorsRoute("GET", "/api/projects/(.+)", getProjectObjects(apiConfig, ctx, user)),
168+ shared.NewCorsRoute("GET", "/api/posts/prose", getPosts(apiConfig, ctx, user, "prose")),
169+ shared.NewCorsRoute("GET", "/api/posts/pastes", getPosts(apiConfig, ctx, user, "pastes")),
170+ shared.NewCorsRoute("GET", "/api/posts/feeds", getPosts(apiConfig, ctx, user, "feeds")),
171 }
172 }