074eada2c8630bfd321216a676c1702f6919a103

Author
Toby Padilla <toby@charm.sh>
Committer
Toby Padilla <toby@charm.sh>
Date

Message

Use new Wish auth WIP

Diff

This diff is truncated to protect this page.

  1diff --git a/config/auth.go b/config/auth.go
  2new file mode 100644
  3index 0000000000000000000000000000000000000000..999262c5a07821e8e08a0b3417814c007977ab41
  4--- /dev/null
  5+++ b/config/auth.go
  6@@ -0,0 +1,20 @@
  7+package config
  8+
  9+import (
 10+	gm "github.com/charmbracelet/wish/git"
 11+	"github.com/gliderlabs/ssh"
 12+)
 13+
 14+func (cfg *Config) AuthRepo(repo string, pk ssh.PublicKey) gm.AccessLevel {
 15+	// TODO: check yaml for access rules
 16+	return gm.ReadWriteAccess
 17+}
 18+
 19+func (cfg *Config) PasswordHandler(ctx ssh.Context, password string) bool {
 20+	return cfg.AnonReadOnly && cfg.AllowNoKeys
 21+}
 22+
 23+func (cfg *Config) PublicKeyHandler(ctx ssh.Context, pk ssh.PublicKey) bool {
 24+	// TODO: check yaml for access rules
 25+	return true
 26+}
 27diff --git a/config/config.go b/config/config.go
 28new file mode 100644
 29index 0000000000000000000000000000000000000000..b64974d8be1017ab8c78129d1c27dd9fa0275dfe
 30--- /dev/null
 31+++ b/config/config.go
 32@@ -0,0 +1,164 @@
 33+package config
 34+
 35+import (
 36+	"log"
 37+
 38+	"gopkg.in/yaml.v2"
 39+
 40+	"fmt"
 41+	"os"
 42+	"path/filepath"
 43+	"soft-serve/git"
 44+
 45+	"github.com/gliderlabs/ssh"
 46+	gg "github.com/go-git/go-git/v5"
 47+	"github.com/go-git/go-git/v5/plumbing/object"
 48+)
 49+
 50+type Config struct {
 51+	Name         string `yaml:"name"`
 52+	Host         string `yaml:"host"`
 53+	Port         int    `yaml:"port"`
 54+	AnonReadOnly bool   `yaml:"anon-access"`
 55+	AllowNoKeys  bool   `yaml:"allow-no-keys"`
 56+	Users        []User `yaml:"users"`
 57+	Repos        []Repo `yaml:"repos"`
 58+	Source       *git.RepoSource
 59+}
 60+
 61+type User struct {
 62+	Name        string   `yaml:"name"`
 63+	Admin       bool     `yaml:"admin"`
 64+	PublicKey   string   `yaml:"pk"`
 65+	CollabRepos []string `yaml:"collab_repos"`
 66+}
 67+
 68+type Repo struct {
 69+	Name string `yaml:"name"`
 70+	Repo string `yaml:"repo"`
 71+	Note string `yaml:"note"`
 72+}
 73+
 74+func NewConfig(host string, port int, anon bool, pk string, rs *git.RepoSource) (*Config, error) {
 75+	cfg := &Config{}
 76+	cfg.Host = host
 77+	cfg.Port = port
 78+	cfg.AnonReadOnly = anon
 79+	cfg.Source = rs
 80+
 81+	var yamlUsers string
 82+	var h string
 83+	if host == "" {
 84+		h = "localhost"
 85+	} else {
 86+		h = host
 87+	}
 88+	yamlConfig := fmt.Sprintf(defaultConfig, h, port, anon)
 89+	if pk != "" {
 90+		yamlUsers = fmt.Sprintf(hasKeyUserConfig, pk)
 91+	} else {
 92+		yamlUsers = defaultUserConfig
 93+	}
 94+	yaml := fmt.Sprintf("%s%s%s", yamlConfig, yamlUsers, exampleUserConfig)
 95+	err := cfg.createDefaultConfigRepo(yaml)
 96+	if err != nil {
 97+		return nil, err
 98+	}
 99+	return cfg, nil
100+}
101+
102+func (cfg *Config) Pushed(repo string, pk ssh.PublicKey) {
103+	err := cfg.Reload()
104+	if err != nil {
105+		log.Printf("error reloading after push: %s", err)
106+	}
107+}
108+
109+func (cfg *Config) Reload() error {
110+	err := cfg.Source.LoadRepos()
111+	if err != nil {
112+		return err
113+	}
114+	cr, err := cfg.Source.GetRepo("config")
115+	if err != nil {
116+		return err
117+	}
118+	cs, err := cr.LatestFile("config.yaml")
119+	if err != nil {
120+		return err
121+	}
122+	err = yaml.Unmarshal([]byte(cs), cfg)
123+	if err != nil {
124+		return fmt.Errorf("bad yaml in config.yaml: %s", err)
125+	}
126+	return nil
127+}
128+
129+func createFile(path string, content string) error {
130+	f, err := os.Create(path)
131+	if err != nil {
132diff --git a/config/defaults.go b/config/defaults.go
133new file mode 100644
134index 0000000000000000000000000000000000000000..1a7cbb3866df8db37deef9ac6e2bff02f0bc45e0
135--- /dev/null
136+++ b/config/defaults.go
137@@ -0,0 +1,42 @@
138+package config
139+
140diff --git a/go.mod b/go.mod
141index 0afb48b770c62697c2bc0f388c6418aca7031f46..c7408bd7120adf343dfb9e4084c5edbbe8d21f2e 100644
142--- a/go.mod
143+++ b/go.mod
144@@ -19,6 +19,7 @@ require (
145 	github.com/muesli/reflow v0.3.0
146 	github.com/muesli/termenv v0.9.0
147 	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
148+	gopkg.in/yaml.v2 v2.3.0
149 )
150 
151 require (
152diff --git a/main.go b/main.go
153index 71288764ed2e2fd5da0ff9e8b50e20499848de3f..92c1eaaccce8147781f2eca97b717ca0eac3cbe7 100644
154--- a/main.go
155+++ b/main.go
156@@ -3,45 +3,62 @@ package main
157 import (
158 	"fmt"
159 	"log"
160+	"soft-serve/config"
161+	"soft-serve/git"
162 	"soft-serve/tui"
163-	"time"
164 
165 	"github.com/charmbracelet/wish"
166 	bm "github.com/charmbracelet/wish/bubbletea"
167 	gm "github.com/charmbracelet/wish/git"
168 	lm "github.com/charmbracelet/wish/logging"
169+	"github.com/gliderlabs/ssh"
170 
171 	"github.com/meowgorithm/babyenv"
172 )
173 
174-type Config struct {
175-	Port         int    `env:"SOFT_SERVE_PORT" default:"23231"`
176-	Host         string `env:"SOFT_SERVE_HOST" default:""`
177-	KeyPath      string `env:"SOFT_SERVE_KEY_PATH" default:".ssh/soft_serve_server_ed25519"`
178-	RepoAuth     string `env:"SOFT_SERVE_REPO_KEYS" default:""`
179-	RepoAuthFile string `env:"SOFT_SERVE_REPO_KEYS_PATH" default:".ssh/soft_serve_git_authorized_keys"`
180-	RepoPath     string `env:"SOFT_SERVE_REPO_PATH" default:".repos"`
181+type serverConfig struct {
182+	Port     int    `env:"SOFT_SERVE_PORT" default:"23231"`
183+	Host     string `env:"SOFT_SERVE_HOST" default:""`
184+	InitKey  string `env:"SOFT_SERVE_REPO_KEY" default:""`
185+	KeyPath  string `env:"SOFT_SERVE_KEY_PATH" default:".ssh/soft_serve_server_ed25519"`
186+	RepoPath string `env:"SOFT_SERVE_REPO_PATH" default:".repos"`
187 }
188 
189 func main() {
190-	var cfg Config
191-	err := babyenv.Parse(&cfg)
192+	var scfg serverConfig
193+	var cfg *config.Config
194+	var err error
195+	err = babyenv.Parse(&scfg)
196 	if err != nil {
197 		log.Fatalln(err)
198 	}
199+	rs := git.NewRepoSource(scfg.RepoPath)
200+	if scfg.InitKey == "" {
201+		cfg, err = config.NewConfig(scfg.Host, scfg.Port, true, "", rs)
202+		if err != nil {
203+			log.Fatalln(err)
204+		}
205+	} else {
206+		cfg, err = config.NewConfig(scfg.Host, scfg.Port, false, scfg.InitKey, rs)
207+		if err != nil {
208+			log.Fatalln(err)
209+		}
210+	}
211 	s, err := wish.NewServer(
212-		wish.WithAddress(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)),
213-		wish.WithHostKeyPath(cfg.KeyPath),
214+		ssh.PublicKeyAuth(cfg.PublicKeyHandler),
215+		ssh.PasswordAuth(cfg.PasswordHandler),
216+		wish.WithAddress(fmt.Sprintf("%s:%d", scfg.Host, scfg.Port)),
217+		wish.WithHostKeyPath(scfg.KeyPath),
218 		wish.WithMiddlewares(
219-			bm.Middleware(tui.SessionHandler(cfg.RepoPath, time.Second*5)),
220-			gm.Middleware(cfg.RepoPath, cfg.RepoAuth, cfg.RepoAuthFile),
221+			bm.Middleware(tui.SessionHandler(cfg)),
222+			gm.MiddlewareWithPushCallback(scfg.RepoPath, cfg, cfg.Pushed),
223 			lm.Middleware(),
224 		),
225 	)
226 	if err != nil {
227 		log.Fatalln(err)
228 	}
229-	log.Printf("Starting SSH server on %s:%d\n", cfg.Host, cfg.Port)
230+	log.Printf("Starting SSH server on %s:%d\n", scfg.Host, scfg.Port)
231 	err = s.ListenAndServe()
232 	if err != nil {
233 		log.Fatalln(err)
234diff --git a/tui/bubble.go b/tui/bubble.go
235index e0ebb9ea62b40f0f5ea0d1f8503b6f4e83304e49..400a249ee7c813f3b3b9119c978e5586442e2ecf 100644
236--- a/tui/bubble.go
237+++ b/tui/bubble.go
238@@ -2,6 +2,7 @@ package tui
239 
240 import (
241 	"fmt"
242+	"soft-serve/config"
243 	"soft-serve/git"
244 	"soft-serve/tui/bubbles/repo"
245 	"soft-serve/tui/bubbles/selection"
246@@ -22,13 +23,10 @@ const (
247 	quitState
248 )
249 
250-type Config struct {
251-	Name         string      `json:"name"`
252-	Host         string      `json:"host"`
253-	Port         int64       `json:"port"`
254-	ShowAllRepos bool        `json:"show_all_repos"`
255-	Menu         []MenuEntry `json:"menu"`
256-	RepoSource   *git.RepoSource
257+type SessionConfig struct {
258+	Width       int
259+	Height      int
260+	InitialRepo string
261 }
262 
263 type MenuEntry struct {
264@@ -38,20 +36,13 @@ type MenuEntry struct {
265 	bubble *repo.Bubble
266 }
267 
268-type SessionConfig struct {
269-	Width       int
270-	Height      int
271-	InitialRepo string
272-}
273-
274 type Bubble struct {
275-	config      *Config
276+	config      *config.Config
277 	styles      *style.Styles
278 	state       sessionState
279 	error       string
280 	width       int
281 	height      int
282-	repoSource  *git.RepoSource
283 	initialRepo string
284 	repoMenu    []MenuEntry
285 	repos       []*git.Repo
286@@ -60,17 +51,12 @@ type Bubble struct {
287 	repoSelect  *selection.Bubble
288 }
289 
290-func NewBubble(cfg *Config, sCfg *SessionConfig) *Bubble {
291-	var repoSource *git.RepoSource = nil
292-	if cfg != nil {
293-		repoSource = cfg.RepoSource
294-	}
295+func NewBubble(cfg *config.Config, sCfg *SessionConfig) *Bubble {
296 	b := &Bubble{
297 		config:      cfg,
298 		styles:      style.DefaultStyles(),
299 		width:       sCfg.Width,
300 		height:      sCfg.Height,
301-		repoSource:  repoSource,
302 		repoMenu:    make([]MenuEntry, 0),
303 		boxes:       make([]tea.Model, 2),
304 		initialRepo: sCfg.InitialRepo,
305diff --git a/tui/bubbles/repo/bubble.go b/tui/bubbles/repo/bubble.go
306index aad6a734ea943ac014f7bf8005a9e30b98f1adae..bf2ac0c591c724b58b391fbb32975980163ce3b1 100644
307--- a/tui/bubbles/repo/bubble.go
308+++ b/tui/bubbles/repo/bubble.go
309@@ -46,7 +46,7 @@ type Bubble struct {
310 	// solution would be to (rename and) move this Bubble into the parent
311 	// package.
312 	Host string
313-	Port int64
314+	Port int
315 }
316 
317 func NewBubble(rs *git.RepoSource, name string, styles *style.Styles, width, wm, height, hm int, tmp interface{}) *Bubble {
318diff --git a/tui/commands.go b/tui/commands.go
319index 4f7c6d45259e13c77632a2f4fb6f43b1c4ccb99d..7f777cc2e40db1418837fd27abfeb1fd4be2a8ee 100644
320--- a/tui/commands.go
321+++ b/tui/commands.go
322@@ -3,6 +3,7 @@ package tui
323 import (
324 	"fmt"
325 	"log"
326+	"soft-serve/config"
327 	"soft-serve/tui/bubbles/repo"
328 	"soft-serve/tui/bubbles/selection"
329 	"time"
330@@ -19,29 +20,27 @@ func (e errMsg) Error() string {
331 }
332 
333 func (b *Bubble) setupCmd() tea.Msg {
334-	if b.config == nil || b.config.RepoSource == nil {
335+	if b.config == nil || b.config.Source == nil {
336 		return errMsg{err: fmt.Errorf("config not set")}
337 	}
338 	ct := time.Now()
339 	lipgloss.SetColorProfile(termenv.ANSI256)
340-	b.repos = b.repoSource.AllRepos()
341-	mes := append([]MenuEntry{}, b.config.Menu...)
342+	b.repos = b.config.Source.AllRepos()
343+	mes := append([]MenuEntry{}, b.repoMenu...)
344 	rs := make([]string, 0)
345-	if b.config.ShowAllRepos {
346-	OUTER:
347-		for _, r := range b.repos {
348-			for _, me := range mes {
349-				if r.Name == me.Repo {
350-					continue OUTER
351-				}
352+OUTER:
353+	for _, r := range b.repos {
354+		for _, me := range mes {
355+			if r.Name == me.Repo {
356+				continue OUTER
357 			}
358-			mes = append(mes, MenuEntry{Name: r.Name, Repo: r.Name})
359 		}
360+		mes = append(mes, MenuEntry{Name: r.Name, Repo: r.Name})
361 	}
362 	if len(mes) == 0 {
363 		return errMsg{fmt.Errorf("no repos found")}
364 	}
365-	var tmplConfig *Config
366+	var tmplConfig *config.Config
367 	for _, me := range mes {
368 		if me.Repo == "config" {
369 			tmplConfig = b.config
370@@ -53,7 +52,7 @@ func (b *Bubble) setupCmd() tea.Msg {
371 			lipgloss.Height(b.footerView()) +
372 			b.styles.RepoBody.GetVerticalFrameSize() +
373 			b.styles.App.GetVerticalMargins()
374-		rb := repo.NewBubble(b.repoSource, me.Repo, b.styles, width, boxLeftWidth, b.height, heightMargin, tmplConfig)
375+		rb := repo.NewBubble(b.config.Source, me.Repo, b.styles, width, boxLeftWidth, b.height, heightMargin, tmplConfig)
376 		rb.Host = b.config.Host
377 		rb.Port = b.config.Port
378 		initCmd := rb.Init()
379diff --git a/tui/session.go b/tui/session.go
380index 78ed852cddbea2cceee98d2015aac07b18c58e52..51eedc361eb876aef7534ae193d0b049cf73767a 100644
381--- a/tui/session.go
382+++ b/tui/session.go
383@@ -1,52 +1,22 @@
384 package tui
385 
386 import (
387-	"encoding/json"
388 	"fmt"
389-	"log"
390-	"soft-serve/git"
391-	"time"
392+	"soft-serve/config"
393 
394 	tea "github.com/charmbracelet/bubbletea"
395 	"github.com/gliderlabs/ssh"
396 )
397 
398-func SessionHandler(reposPath string, repoPoll time.Duration) func(ssh.Session) (tea.Model, []tea.ProgramOption) {
399-	rs := git.NewRepoSource(reposPath)
400-	// createDefaultConfigRepo runs rs.LoadRepos()
401-	err := createDefaultConfigRepo(rs)
402-	if err != nil {
403-		if err != nil {
404-			log.Fatalf("cannot create config repo: %s", err)
405-		}
406-	}
407-	appCfg, err := loadConfig(rs)
408-	if err != nil {
409-		log.Printf("cannot load config: %s", err)
410-	}
411-
412+func SessionHandler(cfg *config.Config) func(ssh.Session) (tea.Model, []tea.ProgramOption) {
413 	return func(s ssh.Session) (tea.Model, []tea.ProgramOption) {
414 		cmd := s.Command()
415-		// reload repos and config on git push
416-		if len(cmd) > 0 && cmd[0] == "git-receive-pack" {
417-			ct := time.Now()
418-			err := rs.LoadRepos()
419-			if err != nil {
420-				log.Printf("cannot load repos: %s", err)
421-			}
422-			cfg, err := loadConfig(rs)
423-			if err != nil {
424-				log.Printf("cannot load config: %s", err)
425-			}
426-			appCfg = cfg
427-			log.Printf("Repo bubble loaded in %s", time.Since(ct))
428-		}
429-		cfg := &SessionConfig{}
430+		scfg := &SessionConfig{}
431 		switch len(cmd) {
432 		case 0:
433-			cfg.InitialRepo = ""
434+			scfg.InitialRepo = ""
435 		case 1:
436-			cfg.InitialRepo = cmd[0]
437+			scfg.InitialRepo = cmd[0]
438 		default:
439 			return nil, nil
440 		}
441@@ -55,26 +25,8 @@ func SessionHandler(reposPath string, repoPoll time.Duration) func(ssh.Session)
442 			fmt.Println("not active")
443 			return nil, nil
444 		}
445-		cfg.Width = pty.Window.Width
446-		cfg.Height = pty.Window.Height
447-		return NewBubble(appCfg, cfg), []tea.ProgramOption{tea.WithAltScreen()}
448-	}
449-}
450-
451-func loadConfig(rs *git.RepoSource) (*Config, error) {
452-	cfg := &Config{}
453-	cfg.RepoSource = rs
454-	cr, err := rs.GetRepo("config")
455-	if err != nil {
456-		return nil, err
457-	}
458-	cs, err := cr.LatestFile("config.json")
459-	if err != nil {
460-		return nil, err
461-	}
462-	err = json.Unmarshal([]byte(cs), cfg)
463-	if err != nil {
464-		return nil, fmt.Errorf("bad json in config.json: %s", err)
465+		scfg.Width = pty.Window.Width
466+		scfg.Height = pty.Window.Height
467+		return NewBubble(cfg, scfg), []tea.ProgramOption{tea.WithAltScreen()}
468 	}
469-	return cfg, nil
470 }