diff --git a/accesscontrol/accesscontrol.go b/accesscontrol/accesscontrol.go index e621d64..5a8b310 100644 --- a/accesscontrol/accesscontrol.go +++ b/accesscontrol/accesscontrol.go @@ -4,14 +4,15 @@ package accesscontrol import ( "fmt" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" ) // Middleware will exit 1 connections trying to execute commands that are not allowed. // If no allowed commands are provided, no commands will be allowed. func Middleware(cmds ...string) wish.Middleware { - return func(sh wish.Handler) wish.Handler { - return func(s wish.Session) { + return func(sh ssh.Handler) ssh.Handler { + return func(s ssh.Session) { if len(s.Command()) == 0 { sh(s) return diff --git a/accesscontrol/accesscontrol_test.go b/accesscontrol/accesscontrol_test.go index 5f0a4b1..580ec97 100644 --- a/accesscontrol/accesscontrol_test.go +++ b/accesscontrol/accesscontrol_test.go @@ -4,10 +4,10 @@ import ( "fmt" "testing" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish/accesscontrol" "github.com/charmbracelet/wish/testsession" - "golang.org/x/crypto/ssh" + gossh "golang.org/x/crypto/ssh" ) const out = "hello world" @@ -77,10 +77,10 @@ func TestMiddleware(t *testing.T) { }) } -func setup(tb testing.TB, allowedCmds ...string) *ssh.Session { +func setup(tb testing.TB, allowedCmds ...string) *gossh.Session { tb.Helper() - return testsession.New(tb, &wish.Server{ - Handler: accesscontrol.Middleware(allowedCmds...)(func(s wish.Session) { + return testsession.New(tb, &ssh.Server{ + Handler: accesscontrol.Middleware(allowedCmds...)(func(s ssh.Session) { s.Write([]byte(out)) }), }, nil) diff --git a/activeterm/activeterm.go b/activeterm/activeterm.go index 8b56864..8ed590c 100644 --- a/activeterm/activeterm.go +++ b/activeterm/activeterm.go @@ -4,13 +4,14 @@ package activeterm import ( "fmt" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" ) // Middleware will exit 1 connections trying with no active terminals. func Middleware() wish.Middleware { - return func(sh wish.Handler) wish.Handler { - return func(s wish.Session) { + return func(sh ssh.Handler) ssh.Handler { + return func(s ssh.Session) { _, _, active := s.Pty() if !active { fmt.Fprintln(s, "Requires an active PTY") diff --git a/activeterm/activeterm_test.go b/activeterm/activeterm_test.go index c3eb8b4..e0cebd3 100644 --- a/activeterm/activeterm_test.go +++ b/activeterm/activeterm_test.go @@ -3,10 +3,10 @@ package activeterm_test import ( "testing" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish/activeterm" "github.com/charmbracelet/wish/testsession" - "golang.org/x/crypto/ssh" + gossh "golang.org/x/crypto/ssh" ) func TestMiddleware(t *testing.T) { @@ -21,10 +21,10 @@ func TestMiddleware(t *testing.T) { }) } -func setup(tb testing.TB) *ssh.Session { +func setup(tb testing.TB) *gossh.Session { tb.Helper() - return testsession.New(tb, &wish.Server{ - Handler: activeterm.Middleware()(func(s wish.Session) { + return testsession.New(tb, &ssh.Server{ + Handler: activeterm.Middleware()(func(s ssh.Session) { s.Write([]byte("hello")) }), }, nil) diff --git a/bubbletea/tea.go b/bubbletea/tea.go index da84a81..942561d 100644 --- a/bubbletea/tea.go +++ b/bubbletea/tea.go @@ -6,6 +6,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" "github.com/muesli/termenv" ) @@ -20,7 +21,7 @@ type BubbleTeaHandler = Handler // nolint: revive // Handler is the function Bubble Tea apps implement to hook into the // SSH Middleware. This will create a new tea.Program for every connection and // start it with the tea.ProgramOptions returned. -type Handler func(wish.Session) (tea.Model, []tea.ProgramOption) +type Handler func(ssh.Session) (tea.Model, []tea.ProgramOption) // ProgramHandler is the function Bubble Tea apps implement to hook into the SSH // Middleware. This should return a new tea.Program. This handler is different @@ -29,7 +30,7 @@ type Handler func(wish.Session) (tea.Model, []tea.ProgramOption) // // Make sure to set the tea.WithInput and tea.WithOutput to the ssh.Session // otherwise the program will not function properly. -type ProgramHandler func(wish.Session) *tea.Program +type ProgramHandler func(ssh.Session) *tea.Program // Middleware takes a Handler and hooks the input and output for the // ssh.Session into the tea.Program. It also captures window resize events and @@ -44,7 +45,7 @@ func Middleware(bth Handler) wish.Middleware { // by an SSH client's terminal cannot be detected by the server but this will // allow for manually setting the color profile on all SSH connections. func MiddlewareWithColorProfile(bth Handler, cp termenv.Profile) wish.Middleware { - h := func(s wish.Session) *tea.Program { + h := func(s ssh.Session) *tea.Program { m, opts := bth(s) if m == nil { return nil @@ -63,9 +64,9 @@ func MiddlewareWithColorProfile(bth Handler, cp termenv.Profile) wish.Middleware // Make sure to set the tea.WithInput and tea.WithOutput to the ssh.Session // otherwise the program will not function properly. func MiddlewareWithProgramHandler(bth ProgramHandler, cp termenv.Profile) wish.Middleware { - return func(sh wish.Handler) wish.Handler { + return func(sh ssh.Handler) ssh.Handler { lipgloss.SetColorProfile(cp) - return func(s wish.Session) { + return func(s ssh.Session) { p := bth(s) if p != nil { _, windowChanges, _ := s.Pty() diff --git a/comment/comment.go b/comment/comment.go index 5f772da..8b45153 100644 --- a/comment/comment.go +++ b/comment/comment.go @@ -1,11 +1,14 @@ package comment -import "github.com/charmbracelet/wish" +import ( + "github.com/charmbracelet/ssh" + "github.com/charmbracelet/wish" +) // Middleware prints a comment at the end of the session. func Middleware(comment string) wish.Middleware { - return func(sh wish.Handler) wish.Handler { - return func(s wish.Session) { + return func(sh ssh.Handler) ssh.Handler { + return func(s ssh.Session) { sh(s) wish.Println(s, comment) } diff --git a/comment/comment_test.go b/comment/comment_test.go index 5375881..841a5e9 100644 --- a/comment/comment_test.go +++ b/comment/comment_test.go @@ -3,9 +3,9 @@ package comment import ( "testing" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish/testsession" - "golang.org/x/crypto/ssh" + gossh "golang.org/x/crypto/ssh" ) func TestMiddleware(t *testing.T) { @@ -18,10 +18,10 @@ func TestMiddleware(t *testing.T) { }) } -func setup(tb testing.TB) *ssh.Session { +func setup(tb testing.TB) *gossh.Session { tb.Helper() - return testsession.New(tb, &wish.Server{ - Handler: Middleware("test")(func(s wish.Session) {}), + return testsession.New(tb, &ssh.Server{ + Handler: Middleware("test")(func(s ssh.Session) {}), }, nil) } diff --git a/elapsed/elapsed.go b/elapsed/elapsed.go index c858bae..5b0885b 100644 --- a/elapsed/elapsed.go +++ b/elapsed/elapsed.go @@ -3,6 +3,7 @@ package timer import ( "time" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" ) @@ -11,8 +12,8 @@ import ( // // This must be called as the last middleware in the chain. func MiddlewareWithFormat(format string) wish.Middleware { - return func(sh wish.Handler) wish.Handler { - return func(s wish.Session) { + return func(sh ssh.Handler) ssh.Handler { + return func(s ssh.Session) { now := time.Now() sh(s) wish.Printf(s, format, time.Since(now)) diff --git a/elapsed/elapsed_test.go b/elapsed/elapsed_test.go index 405c663..efb5305 100644 --- a/elapsed/elapsed_test.go +++ b/elapsed/elapsed_test.go @@ -4,9 +4,9 @@ import ( "testing" "time" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish/testsession" - "golang.org/x/crypto/ssh" + gossh "golang.org/x/crypto/ssh" ) var waitDuration = time.Second @@ -23,10 +23,10 @@ func TestMiddleware(t *testing.T) { }) } -func setup(tb testing.TB) *ssh.Session { +func setup(tb testing.TB) *gossh.Session { tb.Helper() - return testsession.New(tb, &wish.Server{ - Handler: MiddlewareWithFormat("%v")(func(s wish.Session) { + return testsession.New(tb, &ssh.Server{ + Handler: MiddlewareWithFormat("%v")(func(s ssh.Session) { time.Sleep(waitDuration) }), }, nil) diff --git a/examples/bubbletea/main.go b/examples/bubbletea/main.go index f2264b9..cb862d2 100644 --- a/examples/bubbletea/main.go +++ b/examples/bubbletea/main.go @@ -13,6 +13,7 @@ import ( "time" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" bm "github.com/charmbracelet/wish/bubbletea" lm "github.com/charmbracelet/wish/logging" @@ -58,7 +59,7 @@ func main() { // handles the incoming ssh.Session. Here we just grab the terminal info and // pass it to the new model. You can also return tea.ProgramOptions (such as // tea.WithAltScreen) on a session by session basis. -func teaHandler(s wish.Session) (tea.Model, []tea.ProgramOption) { +func teaHandler(s ssh.Session) (tea.Model, []tea.ProgramOption) { pty, _, active := s.Pty() if !active { wish.Fatalln(s, "no active terminal, skipping") diff --git a/examples/bubbleteaprogram/main.go b/examples/bubbleteaprogram/main.go index aff94e8..778c2d6 100644 --- a/examples/bubbleteaprogram/main.go +++ b/examples/bubbleteaprogram/main.go @@ -13,6 +13,7 @@ import ( "time" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" bm "github.com/charmbracelet/wish/bubbletea" lm "github.com/charmbracelet/wish/logging" @@ -68,7 +69,7 @@ func myCustomBubbleteaMiddleware() wish.Middleware { }() return p } - teaHandler := func(s wish.Session) *tea.Program { + teaHandler := func(s ssh.Session) *tea.Program { pty, _, active := s.Pty() if !active { wish.Fatalln(s, "no active terminal, skipping") diff --git a/examples/cobra/main.go b/examples/cobra/main.go index a212264..236ba41 100644 --- a/examples/cobra/main.go +++ b/examples/cobra/main.go @@ -9,6 +9,7 @@ import ( "syscall" "time" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" "github.com/charmbracelet/wish/logging" "github.com/spf13/cobra" @@ -47,8 +48,8 @@ func main() { wish.WithAddress(fmt.Sprintf("%s:%d", host, port)), wish.WithHostKeyPath(".ssh/term_info_ed25519"), wish.WithMiddleware( - func(h wish.Handler) wish.Handler { - return func(s wish.Session) { + func(h ssh.Handler) ssh.Handler { + return func(s ssh.Session) { rootCmd := cmd() rootCmd.SetArgs(s.Command()) rootCmd.SetIn(s) diff --git a/examples/git/main.go b/examples/git/main.go index 9286b52..b2a360b 100644 --- a/examples/git/main.go +++ b/examples/git/main.go @@ -13,6 +13,7 @@ import ( "syscall" "time" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" gm "github.com/charmbracelet/wish/git" lm "github.com/charmbracelet/wish/logging" @@ -28,23 +29,23 @@ type app struct { access gm.AccessLevel } -func (a app) AuthRepo(repo string, pk wish.PublicKey) gm.AccessLevel { +func (a app) AuthRepo(repo string, pk ssh.PublicKey) gm.AccessLevel { return a.access } -func (a app) Push(repo string, pk wish.PublicKey) { +func (a app) Push(repo string, pk ssh.PublicKey) { log.Printf("pushed %s", repo) } -func (a app) Fetch(repo string, pk wish.PublicKey) { +func (a app) Fetch(repo string, pk ssh.PublicKey) { log.Printf("fetch %s", repo) } -func passHandler(ctx wish.Context, password string) bool { +func passHandler(ctx ssh.Context, password string) bool { return false } -func pkHandler(ctx wish.Context, key wish.PublicKey) bool { +func pkHandler(ctx ssh.Context, key ssh.PublicKey) bool { return true } @@ -53,8 +54,8 @@ func main() { a := app{gm.ReadWriteAccess} s, err := wish.NewServer( - wish.WithPublicKeyAuth(pkHandler), - wish.WithPasswordAuth(passHandler), + ssh.PublicKeyAuth(pkHandler), + ssh.PasswordAuth(passHandler), wish.WithAddress(fmt.Sprintf("%s:%d", host, port)), wish.WithHostKeyPath(".ssh/git_server_ed25519"), wish.WithMiddleware( @@ -88,8 +89,8 @@ func main() { // Normally we would use a Bubble Tea program for the TUI but for simplicity, // we'll just write a list of the pushed repos to the terminal and exit the ssh // session. -func gitListMiddleware(h wish.Handler) wish.Handler { - return func(s wish.Session) { +func gitListMiddleware(h ssh.Handler) ssh.Handler { + return func(s ssh.Session) { // Git will have a command included so only run this if there are no // commands passed to ssh. if len(s.Command()) == 0 { diff --git a/examples/go.mod b/examples/go.mod index 4a553b2..fa2373e 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -4,10 +4,10 @@ go 1.18 require ( github.com/charmbracelet/bubbletea v0.23.1 + github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103 github.com/charmbracelet/wish v0.5.0 github.com/muesli/termenv v0.13.0 github.com/spf13/cobra v1.5.0 - golang.org/x/crypto v0.3.0 ) require ( @@ -19,7 +19,6 @@ require ( github.com/caarlos0/sshmarshal v0.1.0 // indirect github.com/charmbracelet/keygen v0.3.0 // indirect github.com/charmbracelet/lipgloss v0.6.0 // indirect - github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103 // indirect github.com/containerd/console v1.0.3 // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/go-git/gcfg v1.5.0 // indirect @@ -41,10 +40,11 @@ require ( github.com/sergi/go-diff v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect - golang.org/x/net v0.2.0 // indirect - golang.org/x/sys v0.2.0 // indirect - golang.org/x/term v0.2.0 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect + golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect + golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect + golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect + golang.org/x/text v0.3.7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/examples/go.sum b/examples/go.sum index cba6223..9a5bdf7 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -32,8 +32,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= @@ -116,14 +116,13 @@ github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6e golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -138,19 +137,16 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/examples/identity/main.go b/examples/identity/main.go index 4bbb8af..687e804 100644 --- a/examples/identity/main.go +++ b/examples/identity/main.go @@ -9,9 +9,9 @@ import ( "syscall" "time" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" "github.com/charmbracelet/wish/logging" - "golang.org/x/crypto/ssh" ) const ( @@ -23,18 +23,18 @@ func main() { s, err := wish.NewServer( wish.WithAddress(fmt.Sprintf("%s:%d", host, port)), wish.WithHostKeyPath(".ssh/term_info_ed25519"), - wish.WithPublicKeyAuth(func(ctx wish.Context, key wish.PublicKey) bool { + wish.WithPublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool { return true }), wish.WithMiddleware( logging.Middleware(), - func(h wish.Handler) wish.Handler { - return func(s wish.Session) { + func(h ssh.Handler) ssh.Handler { + return func(s ssh.Session) { carlos, _, _, _, _ := ssh.ParseAuthorizedKey( []byte("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILxWe2rXKoiO6W14LYPVfJKzRfJ1f3Jhzxrgjc/D4tU7"), ) switch { - case wish.KeysEqual(s.PublicKey(), carlos): + case ssh.KeysEqual(s.PublicKey(), carlos): wish.Println(s, "Hey Carlos!") default: wish.Println(s, "Hey, I don't know who you are!") diff --git a/examples/simple/main.go b/examples/simple/main.go index 6064899..9d02fae 100644 --- a/examples/simple/main.go +++ b/examples/simple/main.go @@ -9,6 +9,7 @@ import ( "syscall" "time" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" "github.com/charmbracelet/wish/logging" ) @@ -23,8 +24,8 @@ func main() { wish.WithAddress(fmt.Sprintf("%s:%d", host, port)), wish.WithHostKeyPath(".ssh/term_info_ed25519"), wish.WithMiddleware( - func(h wish.Handler) wish.Handler { - return func(s wish.Session) { + func(h ssh.Handler) ssh.Handler { + return func(s ssh.Session) { wish.Println(s, "Hello, world!") h(s) } diff --git a/git/git.go b/git/git.go index d550e12..b26a26e 100644 --- a/git/git.go +++ b/git/git.go @@ -9,6 +9,7 @@ import ( "path/filepath" "strings" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" @@ -53,9 +54,9 @@ type GitHooks = Hooks // nolint: revive // AuthRepo will be called with the ssh.Session public key and the repo name. // Implementers return the appropriate AccessLevel. type Hooks interface { - AuthRepo(string, wish.PublicKey) AccessLevel - Push(string, wish.PublicKey) - Fetch(string, wish.PublicKey) + AuthRepo(string, ssh.PublicKey) AccessLevel + Push(string, ssh.PublicKey) + Fetch(string, ssh.PublicKey) } // Middleware adds Git server functionality to the ssh.Server. Repos are stored @@ -64,8 +65,8 @@ type Hooks interface { // Hooks.Push and Hooks.Fetch will be called on successful completion of // their commands. func Middleware(repoDir string, gh Hooks) wish.Middleware { - return func(sh wish.Handler) wish.Handler { - return func(s wish.Session) { + return func(sh ssh.Handler) ssh.Handler { + return func(s ssh.Session) { cmd := s.Command() if len(cmd) == 2 { gc := cmd[0] @@ -116,7 +117,7 @@ func Middleware(repoDir string, gh Hooks) wish.Middleware { } } -func gitPack(s wish.Session, gitCmd string, repoDir string, repo string) error { +func gitPack(s ssh.Session, gitCmd string, repoDir string, repo string) error { cmd := strings.TrimPrefix(gitCmd, "git-") rp := filepath.Join(repoDir, repo) switch gitCmd { @@ -161,7 +162,7 @@ func fileExists(path string) (bool, error) { } // Fatal prints to the session's STDOUT as a git response and exit 1. -func Fatal(s wish.Session, v ...interface{}) { +func Fatal(s ssh.Session, v ...interface{}) { msg := fmt.Sprint(v...) // hex length includes 4 byte length prefix and ending newline pktLine := fmt.Sprintf("%04x%s\n", len(msg)+5, msg) @@ -194,7 +195,7 @@ func ensureRepo(dir string, repo string) error { return nil } -func runGit(s wish.Session, dir string, args ...string) error { +func runGit(s ssh.Session, dir string, args ...string) error { usi := exec.CommandContext(s.Context(), "git", args...) usi.Dir = dir usi.Stdout = s @@ -205,7 +206,7 @@ func runGit(s wish.Session, dir string, args ...string) error { return nil } -func ensureDefaultBranch(s wish.Session, repoPath string) error { +func ensureDefaultBranch(s ssh.Session, repoPath string) error { r, err := git.PlainOpen(repoPath) if err != nil { return err diff --git a/git/git_test.go b/git/git_test.go index b43b4d7..0fd53f8 100644 --- a/git/git_test.go +++ b/git/git_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/charmbracelet/keygen" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" - "golang.org/x/crypto/ssh" ) func TestGitMiddleware(t *testing.T) { @@ -39,7 +39,7 @@ func TestGitMiddleware(t *testing.T) { } srv, err := wish.NewServer( wish.WithMiddleware(Middleware(repoDir, hooks)), - wish.WithPublicKeyAuth(func(ctx wish.Context, key wish.PublicKey) bool { + wish.WithPublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool { return true }), ) @@ -176,18 +176,18 @@ func requireError(t *testing.T, err error) { } } -func requireHasAction(t *testing.T, actions []action, key wish.PublicKey, repo string) { +func requireHasAction(t *testing.T, actions []action, key ssh.PublicKey, repo string) { t.Helper() for _, action := range actions { - if repo == action.repo && wish.KeysEqual(key, action.key) { + if repo == action.repo && ssh.KeysEqual(key, action.key) { return } } t.Fatalf("expected action for %q, got none", repo) } -func createKeyPair(t *testing.T) (wish.PublicKey, string) { +func createKeyPair(t *testing.T) (ssh.PublicKey, string) { t.Helper() keyDir := t.TempDir() @@ -202,13 +202,13 @@ func createKeyPair(t *testing.T) (wish.PublicKey, string) { } type accessDetails struct { - key wish.PublicKey + key ssh.PublicKey repo string level AccessLevel } type action struct { - key wish.PublicKey + key ssh.PublicKey repo string } @@ -219,23 +219,23 @@ type testHooks struct { access []accessDetails } -func (h *testHooks) AuthRepo(repo string, key wish.PublicKey) AccessLevel { +func (h *testHooks) AuthRepo(repo string, key ssh.PublicKey) AccessLevel { for _, dets := range h.access { - if dets.repo == repo && wish.KeysEqual(key, dets.key) { + if dets.repo == repo && ssh.KeysEqual(key, dets.key) { return dets.level } } return NoAccess } -func (h *testHooks) Push(repo string, key wish.PublicKey) { +func (h *testHooks) Push(repo string, key ssh.PublicKey) { h.Lock() defer h.Unlock() h.pushes = append(h.pushes, action{key, repo}) } -func (h *testHooks) Fetch(repo string, key wish.PublicKey) { +func (h *testHooks) Fetch(repo string, key ssh.PublicKey) { h.Lock() defer h.Unlock() diff --git a/go.mod b/go.mod index db072bd..9ffb7f6 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.1 github.com/matryer/is v1.4.0 github.com/muesli/termenv v0.13.0 - golang.org/x/crypto v0.3.0 + golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/time v0.0.0-20220411224347-583f2d630306 ) @@ -25,6 +25,7 @@ require ( github.com/caarlos0/sshmarshal v0.1.0 // indirect github.com/containerd/console v1.0.3 // indirect github.com/emirpasic/gods v1.12.0 // indirect + github.com/gliderlabs/ssh v0.3.5 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect github.com/google/go-cmp v0.5.5 // indirect @@ -42,9 +43,9 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect - golang.org/x/net v0.2.0 // indirect - golang.org/x/sys v0.2.0 // indirect - golang.org/x/term v0.2.0 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect + golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect + golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect + golang.org/x/text v0.3.7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 7faab92..7335aef 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,9 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= @@ -112,14 +113,13 @@ github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6e golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -135,20 +135,18 @@ golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/logging/logging.go b/logging/logging.go index b1b0604..3c51709 100644 --- a/logging/logging.go +++ b/logging/logging.go @@ -4,6 +4,7 @@ import ( "log" "time" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" ) @@ -27,8 +28,8 @@ type Logger interface { // auth was public key based. Disconnect will log the remote address and // connection duration. func MiddlewareWithLogger(l Logger) wish.Middleware { - return func(sh wish.Handler) wish.Handler { - return func(s wish.Session) { + return func(sh ssh.Handler) ssh.Handler { + return func(s ssh.Session) { ct := time.Now() hpk := s.PublicKey() != nil pty, _, _ := s.Pty() diff --git a/logging/logging_test.go b/logging/logging_test.go index 435e837..9dcb6e4 100644 --- a/logging/logging_test.go +++ b/logging/logging_test.go @@ -3,10 +3,10 @@ package logging_test import ( "testing" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish/logging" "github.com/charmbracelet/wish/testsession" - "golang.org/x/crypto/ssh" + gossh "golang.org/x/crypto/ssh" ) func TestMiddleware(t *testing.T) { @@ -17,10 +17,10 @@ func TestMiddleware(t *testing.T) { }) } -func setup(tb testing.TB) *ssh.Session { +func setup(tb testing.TB) *gossh.Session { tb.Helper() - return testsession.New(tb, &wish.Server{ - Handler: logging.Middleware()(func(s wish.Session) { + return testsession.New(tb, &ssh.Server{ + Handler: logging.Middleware()(func(s ssh.Session) { s.Write([]byte("hello")) }), }, nil) diff --git a/options.go b/options.go index 8d8aec1..da59a7e 100644 --- a/options.go +++ b/options.go @@ -17,16 +17,16 @@ import ( ) // WithAddress returns an ssh.Option that sets the address to listen on. -func WithAddress(addr string) Option { - return func(s *Server) error { +func WithAddress(addr string) ssh.Option { + return func(s *ssh.Server) error { s.Addr = addr return nil } } // WithVersion returns an ssh.Option that sets the server version. -func WithVersion(version string) Option { - return func(s *Server) error { +func WithVersion(version string) ssh.Option { + return func(s *ssh.Server) error { s.Version = version return nil } @@ -37,9 +37,9 @@ func WithVersion(version string) Option { // Server.Handler. // // Notice that middlewares are composed from first to last, which means the last one is executed first. -func WithMiddleware(mw ...Middleware) Option { - return func(s *Server) error { - h := func(s Session) {} +func WithMiddleware(mw ...Middleware) ssh.Option { + return func(s *ssh.Server) error { + h := func(s ssh.Session) {} for _, m := range mw { h = m(h) } @@ -49,35 +49,35 @@ func WithMiddleware(mw ...Middleware) Option { } // WithHostKeyFile returns an ssh.Option that sets the path to the private. -func WithHostKeyPath(path string) Option { +func WithHostKeyPath(path string) ssh.Option { if _, err := os.Stat(path); os.IsNotExist(err) { dir, f := filepath.Split(path) n := strings.TrimSuffix(f, "_ed25519") _, err := keygen.NewWithWrite(filepath.Join(dir, n), nil, keygen.Ed25519) if err != nil { - return func(*Server) error { + return func(*ssh.Server) error { return err } } path = filepath.Join(dir, n+"_ed25519") } - return HostKeyFile(path) + return ssh.HostKeyFile(path) } // WithHostKeyPEM returns an ssh.Option that sets the host key from a PEM block. -func WithHostKeyPEM(pem []byte) Option { - return HostKeyPEM(pem) +func WithHostKeyPEM(pem []byte) ssh.Option { + return ssh.HostKeyPEM(pem) } // WithAuthorizedKeys allows to use a SSH authorized_keys file to allowlist users. -func WithAuthorizedKeys(path string) Option { - return func(s *Server) error { +func WithAuthorizedKeys(path string) ssh.Option { + return func(s *ssh.Server) error { if _, err := os.Stat(path); err != nil { return err } - return WithPublicKeyAuth(func(_ Context, key PublicKey) bool { - return isAuthorized(path, func(k PublicKey) bool { - return KeysEqual(key, k) + return WithPublicKeyAuth(func(_ ssh.Context, key ssh.PublicKey) bool { + return isAuthorized(path, func(k ssh.PublicKey) bool { + return ssh.KeysEqual(key, k) }) })(s) } @@ -86,12 +86,12 @@ func WithAuthorizedKeys(path string) Option { // WithTrustedUserCAKeys authorize certificates that are signed with the given // Certificate Authority public key, and are valid. // Analogous to the TrustedUserCAKeys OpenSSH option. -func WithTrustedUserCAKeys(path string) Option { - return func(s *Server) error { +func WithTrustedUserCAKeys(path string) ssh.Option { + return func(s *ssh.Server) error { if _, err := os.Stat(path); err != nil { return err } - return WithPublicKeyAuth(func(ctx Context, key PublicKey) bool { + return WithPublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool { cert, ok := key.(*gossh.Certificate) if !ok { // not a certificate... @@ -120,7 +120,7 @@ func WithTrustedUserCAKeys(path string) Option { } } -func isAuthorized(path string, checker func(k PublicKey) bool) bool { +func isAuthorized(path string, checker func(k ssh.PublicKey) bool) bool { f, err := os.Open(path) if err != nil { log.Printf("failed to parse %q: %s", path, err) @@ -157,37 +157,31 @@ func isAuthorized(path string, checker func(k PublicKey) bool) bool { } // WithPublicKeyAuth returns an ssh.Option that sets the public key auth handler. -func WithPublicKeyAuth(h PublicKeyHandler) Option { - return ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool { - return h(ctx, key) - }) +func WithPublicKeyAuth(h ssh.PublicKeyHandler) ssh.Option { + return ssh.PublicKeyAuth(h) } // WithPasswordAuth returns an ssh.Option that sets the password auth handler. -func WithPasswordAuth(p PasswordHandler) Option { - return ssh.PasswordAuth(func(ctx ssh.Context, password string) bool { - return p(ctx, password) - }) +func WithPasswordAuth(p ssh.PasswordHandler) ssh.Option { + return ssh.PasswordAuth(p) } // WithKeyboardInteractiveAuth returns an ssh.Option that sets the keyboard interactive auth handler. -func WithKeyboardInteractiveAuth(h KeyboardInteractiveHandler) Option { - return ssh.KeyboardInteractiveAuth(func(ctx ssh.Context, challenger gossh.KeyboardInteractiveChallenge) bool { - return h(ctx, challenger) - }) +func WithKeyboardInteractiveAuth(h ssh.KeyboardInteractiveHandler) ssh.Option { + return ssh.KeyboardInteractiveAuth(h) } // WithIdleTimeout returns an ssh.Option that sets the connection's idle timeout. -func WithIdleTimeout(d time.Duration) Option { - return func(s *Server) error { +func WithIdleTimeout(d time.Duration) ssh.Option { + return func(s *ssh.Server) error { s.IdleTimeout = d return nil } } // WithMaxTimeout returns an ssh.Option that sets the connection's absolute timeout. -func WithMaxTimeout(d time.Duration) Option { - return func(s *Server) error { +func WithMaxTimeout(d time.Duration) ssh.Option { + return func(s *ssh.Server) error { s.MaxTimeout = d return nil } diff --git a/options_test.go b/options_test.go index 88608f0..99e18ae 100755 --- a/options_test.go +++ b/options_test.go @@ -8,39 +8,40 @@ import ( "testing" "time" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish/testsession" - "golang.org/x/crypto/ssh" + gossh "golang.org/x/crypto/ssh" ) func TestWithIdleTimeout(t *testing.T) { - s := Server{} + s := ssh.Server{} requireNoError(t, WithIdleTimeout(time.Second)(&s)) requireEqual(t, time.Second, s.IdleTimeout) } func TestWithMaxTimeout(t *testing.T) { - s := Server{} + s := ssh.Server{} requireNoError(t, WithMaxTimeout(time.Second)(&s)) requireEqual(t, time.Second, s.MaxTimeout) } func TestIsAuthorized(t *testing.T) { t.Run("valid", func(t *testing.T) { - requireEqual(t, true, isAuthorized("testdata/authorized_keys", func(k PublicKey) bool { return true })) + requireEqual(t, true, isAuthorized("testdata/authorized_keys", func(k ssh.PublicKey) bool { return true })) }) t.Run("invalid", func(t *testing.T) { - requireEqual(t, false, isAuthorized("testdata/invalid_authorized_keys", func(k PublicKey) bool { return true })) + requireEqual(t, false, isAuthorized("testdata/invalid_authorized_keys", func(k ssh.PublicKey) bool { return true })) }) t.Run("file not found", func(t *testing.T) { - requireEqual(t, false, isAuthorized("testdata/nope_authorized_keys", func(k PublicKey) bool { return true })) + requireEqual(t, false, isAuthorized("testdata/nope_authorized_keys", func(k ssh.PublicKey) bool { return true })) }) } func TestWithAuthorizedKeys(t *testing.T) { t.Run("valid", func(t *testing.T) { - s := Server{} + s := ssh.Server{} requireNoError(t, WithAuthorizedKeys("testdata/authorized_keys")(&s)) for key, authorize := range map[string]bool{ @@ -57,7 +58,7 @@ func TestWithAuthorizedKeys(t *testing.T) { }) t.Run("invalid", func(t *testing.T) { - s := Server{} + s := ssh.Server{} requireNoError( t, WithAuthorizedKeys("testdata/invalid_authorized_keys")(&s), @@ -65,7 +66,7 @@ func TestWithAuthorizedKeys(t *testing.T) { }) t.Run("file not found", func(t *testing.T) { - s := Server{} + s := ssh.Server{} if err := WithAuthorizedKeys("testdata/nope_authorized_keys")(&s); err == nil { t.Fatal("expected an error, got nil") } @@ -73,33 +74,33 @@ func TestWithAuthorizedKeys(t *testing.T) { } func TestWithTrustedUserCAKeys(t *testing.T) { - setup := func(tb testing.TB, certPath string) (*Server, *ssh.ClientConfig) { - s := &Server{ - Handler: func(s Session) { - cert, ok := s.PublicKey().(*ssh.Certificate) + setup := func(tb testing.TB, certPath string) (*ssh.Server, *gossh.ClientConfig) { + s := &ssh.Server{ + Handler: func(s ssh.Session) { + cert, ok := s.PublicKey().(*gossh.Certificate) fmt.Fprintf(s, "cert? %v - principals: %v - type: %v", ok, cert.ValidPrincipals, cert.CertType) }, } requireNoError(t, WithTrustedUserCAKeys("testdata/ca.pub")(s)) - signer, err := ssh.ParsePrivateKey(getBytes(t, "testdata/foo")) + signer, err := gossh.ParsePrivateKey(getBytes(t, "testdata/foo")) requireNoError(t, err) - cert, _, _, _, err := ssh.ParseAuthorizedKey(getBytes(t, certPath)) + cert, _, _, _, err := gossh.ParseAuthorizedKey(getBytes(t, certPath)) requireNoError(t, err) - certSigner, err := ssh.NewCertSigner(cert.(*ssh.Certificate), signer) + certSigner, err := gossh.NewCertSigner(cert.(*gossh.Certificate), signer) requireNoError(t, err) - return s, &ssh.ClientConfig{ + return s, &gossh.ClientConfig{ User: "foo", - Auth: []ssh.AuthMethod{ - ssh.PublicKeys(certSigner), + Auth: []gossh.AuthMethod{ + gossh.PublicKeys(certSigner), }, } } t.Run("invalid ca key", func(t *testing.T) { - s := &Server{} + s := &ssh.Server{} if err := WithTrustedUserCAKeys("testdata/invalid-path")(s); err == nil { t.Fatal("expected an error, got nil") } @@ -134,20 +135,20 @@ func TestWithTrustedUserCAKeys(t *testing.T) { }) t.Run("not a cert", func(t *testing.T) { - s := &Server{ - Handler: func(s Session) { + s := &ssh.Server{ + Handler: func(s ssh.Session) { fmt.Fprintln(s, "hello") }, } requireNoError(t, WithTrustedUserCAKeys("testdata/ca.pub")(s)) - signer, err := ssh.ParsePrivateKey(getBytes(t, "testdata/foo")) + signer, err := gossh.ParsePrivateKey(getBytes(t, "testdata/foo")) requireNoError(t, err) - _, err = testsession.NewClientSession(t, testsession.Listen(t, s), &ssh.ClientConfig{ + _, err = testsession.NewClientSession(t, testsession.Listen(t, s), &gossh.ClientConfig{ User: "foo", - Auth: []ssh.AuthMethod{ - ssh.PublicKeys(signer), + Auth: []gossh.AuthMethod{ + gossh.PublicKeys(signer), }, }) requireAuthError(t, err) diff --git a/ratelimiter/ratelimiter.go b/ratelimiter/ratelimiter.go index eddf1a9..d7b0a20 100644 --- a/ratelimiter/ratelimiter.go +++ b/ratelimiter/ratelimiter.go @@ -6,6 +6,7 @@ import ( "log" "net" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" lru "github.com/hashicorp/golang-lru/v2" "golang.org/x/time/rate" @@ -19,13 +20,13 @@ var ErrRateLimitExceeded = errors.New("rate limit exceeded, please try again lat // Its up to the implementation to handle what identifies an session as well // as the implementation details of these limits. type RateLimiter interface { - Allow(s wish.Session) error + Allow(s ssh.Session) error } // Middleware provides a new rate limiting Middleware. func Middleware(limiter RateLimiter) wish.Middleware { - return func(sh wish.Handler) wish.Handler { - return func(s wish.Session) { + return func(sh ssh.Handler) ssh.Handler { + return func(s ssh.Session) { if err := limiter.Allow(s); err != nil { wish.Fatal(s, err) return @@ -61,7 +62,7 @@ type limiters struct { burst int } -func (r *limiters) Allow(s wish.Session) error { +func (r *limiters) Allow(s ssh.Session) error { var key string switch addr := s.RemoteAddr().(type) { case *net.TCPAddr: diff --git a/ratelimiter/ratelimiter_test.go b/ratelimiter/ratelimiter_test.go index e461d53..018c38b 100644 --- a/ratelimiter/ratelimiter_test.go +++ b/ratelimiter/ratelimiter_test.go @@ -4,15 +4,15 @@ import ( "testing" "time" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish/testsession" "golang.org/x/sync/errgroup" "golang.org/x/time/rate" ) func TestRateLimiterNoLimit(t *testing.T) { - s := &wish.Server{ - Handler: Middleware(NewRateLimiter(rate.Limit(0), 0, 5))(func(s wish.Session) { + s := &ssh.Server{ + Handler: Middleware(NewRateLimiter(rate.Limit(0), 0, 5))(func(s ssh.Session) { s.Write([]byte("hello")) }), } @@ -24,8 +24,8 @@ func TestRateLimiterNoLimit(t *testing.T) { } func TestRateLimiterZeroedMaxEntried(t *testing.T) { - s := &wish.Server{ - Handler: Middleware(NewRateLimiter(rate.Limit(1), 1, 0))(func(s wish.Session) { + s := &ssh.Server{ + Handler: Middleware(NewRateLimiter(rate.Limit(1), 1, 0))(func(s ssh.Session) { s.Write([]byte("hello")) }), } @@ -37,8 +37,8 @@ func TestRateLimiterZeroedMaxEntried(t *testing.T) { } func TestRateLimiter(t *testing.T) { - s := &wish.Server{ - Handler: Middleware(NewRateLimiter(rate.Limit(10), 4, 1))(func(s wish.Session) { + s := &ssh.Server{ + Handler: Middleware(NewRateLimiter(rate.Limit(10), 4, 1))(func(s ssh.Session) { // noop }), } diff --git a/recover/recover.go b/recover/recover.go index aeaa1cc..8ef8a80 100644 --- a/recover/recover.go +++ b/recover/recover.go @@ -4,6 +4,7 @@ import ( "log" "runtime/debug" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" ) @@ -18,12 +19,12 @@ func MiddlewareWithLogger(logger *log.Logger, mw ...wish.Middleware) wish.Middle if logger == nil { logger = log.Default() } - h := func(wish.Session) {} + h := func(ssh.Session) {} for _, m := range mw { h = m(h) } - return func(sh wish.Handler) wish.Handler { - return func(s wish.Session) { + return func(sh ssh.Handler) ssh.Handler { + return func(s ssh.Session) { func() { defer func() { if r := recover(); r != nil { diff --git a/recover/recover_test.go b/recover/recover_test.go index 753b30e..2eac95c 100644 --- a/recover/recover_test.go +++ b/recover/recover_test.go @@ -3,9 +3,9 @@ package recover import ( "testing" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish/testsession" - "golang.org/x/crypto/ssh" + gossh "golang.org/x/crypto/ssh" ) func TestMiddleware(t *testing.T) { @@ -15,14 +15,14 @@ func TestMiddleware(t *testing.T) { }) } -func setup(tb testing.TB) *ssh.Session { +func setup(tb testing.TB) *gossh.Session { tb.Helper() - return testsession.New(tb, &wish.Server{ - Handler: Middleware(func(h wish.Handler) wish.Handler { - return func(s wish.Session) { + return testsession.New(tb, &ssh.Server{ + Handler: Middleware(func(h ssh.Handler) ssh.Handler { + return func(s ssh.Session) { panic("hello") } - })(func(s wish.Session) {}), + })(func(s ssh.Session) {}), }, nil) } diff --git a/scp/copy_from_client.go b/scp/copy_from_client.go index aa15f1e..0cfd29d 100644 --- a/scp/copy_from_client.go +++ b/scp/copy_from_client.go @@ -10,7 +10,7 @@ import ( "regexp" "strconv" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" ) var ( @@ -27,7 +27,7 @@ func (e parseError) Error() string { return fmt.Sprintf("failed to parse: %q", e.subject) } -func copyFromClient(s wish.Session, info Info, handler CopyFromClientHandler) error { +func copyFromClient(s ssh.Session, info Info, handler CopyFromClientHandler) error { // accepts the request _, _ = s.Write(NULL) diff --git a/scp/copy_to_client.go b/scp/copy_to_client.go index 5fb5d28..3aaf19c 100644 --- a/scp/copy_to_client.go +++ b/scp/copy_to_client.go @@ -4,10 +4,10 @@ import ( "fmt" "io/fs" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" ) -func copyToClient(s wish.Session, info Info, handler CopyToClientHandler) error { +func copyToClient(s ssh.Session, info Info, handler CopyToClientHandler) error { matches, err := handler.Glob(s, info.Path) if err != nil { return err diff --git a/scp/filesystem.go b/scp/filesystem.go index a0376ae..21b2cf3 100644 --- a/scp/filesystem.go +++ b/scp/filesystem.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" ) type fileSystemHandler struct{ root string } @@ -45,7 +45,7 @@ func (h *fileSystemHandler) prefixed(path string) string { return filepath.Join(h.root, path) } -func (h *fileSystemHandler) Glob(_ wish.Session, s string) ([]string, error) { +func (h *fileSystemHandler) Glob(_ ssh.Session, s string) ([]string, error) { matches, err := filepath.Glob(h.prefixed(s)) if err != nil { return []string{}, err @@ -60,11 +60,11 @@ func (h *fileSystemHandler) Glob(_ wish.Session, s string) ([]string, error) { return matches, nil } -func (h *fileSystemHandler) WalkDir(_ wish.Session, path string, fn fs.WalkDirFunc) error { +func (h *fileSystemHandler) WalkDir(_ ssh.Session, path string, fn fs.WalkDirFunc) error { return filepath.WalkDir(h.prefixed(path), fn) } -func (h *fileSystemHandler) NewDirEntry(_ wish.Session, name string) (*DirEntry, error) { +func (h *fileSystemHandler) NewDirEntry(_ ssh.Session, name string) (*DirEntry, error) { path := h.prefixed(name) info, err := os.Stat(path) if err != nil { @@ -80,7 +80,7 @@ func (h *fileSystemHandler) NewDirEntry(_ wish.Session, name string) (*DirEntry, }, nil } -func (h *fileSystemHandler) NewFileEntry(_ wish.Session, name string) (*FileEntry, func() error, error) { +func (h *fileSystemHandler) NewFileEntry(_ ssh.Session, name string) (*FileEntry, func() error, error) { path := h.prefixed(name) info, err := os.Stat(path) if err != nil { @@ -101,14 +101,14 @@ func (h *fileSystemHandler) NewFileEntry(_ wish.Session, name string) (*FileEntr }, f.Close, nil } -func (h *fileSystemHandler) Mkdir(_ wish.Session, entry *DirEntry) error { +func (h *fileSystemHandler) Mkdir(_ ssh.Session, entry *DirEntry) error { if err := os.Mkdir(h.prefixed(entry.Filepath), entry.Mode); err != nil { return fmt.Errorf("failed to create dir: %q: %w", entry.Filepath, err) } return h.chtimes(entry.Filepath, entry.Mtime, entry.Atime) } -func (h *fileSystemHandler) Write(_ wish.Session, entry *FileEntry) (int64, error) { +func (h *fileSystemHandler) Write(_ ssh.Session, entry *FileEntry) (int64, error) { f, err := os.OpenFile(h.prefixed(entry.Filepath), os.O_TRUNC|os.O_RDWR|os.O_CREATE, entry.Mode) if err != nil { return 0, fmt.Errorf("failed to open file: %q: %w", entry.Filepath, err) diff --git a/scp/fs.go b/scp/fs.go index 541529e..074dfbc 100644 --- a/scp/fs.go +++ b/scp/fs.go @@ -4,7 +4,7 @@ import ( "fmt" "io/fs" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" ) type fsHandler struct{ fsys fs.FS } @@ -17,15 +17,15 @@ func NewFSReadHandler(fsys fs.FS) CopyToClientHandler { return &fsHandler{fsys: fsys} } -func (h *fsHandler) Glob(_ wish.Session, s string) ([]string, error) { +func (h *fsHandler) Glob(_ ssh.Session, s string) ([]string, error) { return fs.Glob(h.fsys, s) } -func (h *fsHandler) WalkDir(_ wish.Session, path string, fn fs.WalkDirFunc) error { +func (h *fsHandler) WalkDir(_ ssh.Session, path string, fn fs.WalkDirFunc) error { return fs.WalkDir(h.fsys, path, fn) } -func (h *fsHandler) NewDirEntry(_ wish.Session, path string) (*DirEntry, error) { +func (h *fsHandler) NewDirEntry(_ ssh.Session, path string) (*DirEntry, error) { info, err := fs.Stat(h.fsys, path) if err != nil { return nil, fmt.Errorf("failed to open dir: %q: %w", path, err) @@ -40,7 +40,7 @@ func (h *fsHandler) NewDirEntry(_ wish.Session, path string) (*DirEntry, error) }, nil } -func (h *fsHandler) NewFileEntry(_ wish.Session, path string) (*FileEntry, func() error, error) { +func (h *fsHandler) NewFileEntry(_ ssh.Session, path string) (*FileEntry, func() error, error) { info, err := fs.Stat(h.fsys, path) if err != nil { return nil, nil, fmt.Errorf("failed to stat %q: %w", path, err) diff --git a/scp/scp.go b/scp/scp.go index 9fc45a7..73610a8 100644 --- a/scp/scp.go +++ b/scp/scp.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" ) @@ -22,17 +23,17 @@ type CopyToClientHandler interface { // // Note: if your other functions expect a relative path, make sure that // your Glob implementation returns relative paths as well. - Glob(wish.Session, string) ([]string, error) + Glob(ssh.Session, string) ([]string, error) // WalkDir must be implemented if you want to allow recursive copies. - WalkDir(wish.Session, string, fs.WalkDirFunc) error + WalkDir(ssh.Session, string, fs.WalkDirFunc) error // NewDirEntry should provide a *DirEntry for the given path. - NewDirEntry(wish.Session, string) (*DirEntry, error) + NewDirEntry(ssh.Session, string) (*DirEntry, error) // NewFileEntry should provide a *FileEntry for the given path. // Users may also provide a closing function. - NewFileEntry(wish.Session, string) (*FileEntry, func() error, error) + NewFileEntry(ssh.Session, string) (*FileEntry, func() error, error) } // CopyFromClientHandler is a handler that can be implemented to handle files @@ -40,10 +41,10 @@ type CopyToClientHandler interface { type CopyFromClientHandler interface { // Mkdir should created the given dir. // Note that this usually shouldn't use os.MkdirAll and the like. - Mkdir(wish.Session, *DirEntry) error + Mkdir(ssh.Session, *DirEntry) error // Write should write the given file. - Write(wish.Session, *FileEntry) (int64, error) + Write(ssh.Session, *FileEntry) (int64, error) } // Handler is a interface that can be implemented to handle both SCP @@ -56,8 +57,8 @@ type Handler interface { // Middleware provides a wish middleware using the given CopyToClientHandler // and CopyFromClientHandler. func Middleware(rh CopyToClientHandler, wh CopyFromClientHandler) wish.Middleware { - return func(sh wish.Handler) wish.Handler { - return func(s wish.Session) { + return func(sh ssh.Handler) ssh.Handler { + return func(s ssh.Session) { info := GetInfo(s.Command()) if !info.Ok { sh(s) diff --git a/scp/scp_test.go b/scp/scp_test.go index 618292a..fbe1e54 100644 --- a/scp/scp_test.go +++ b/scp/scp_test.go @@ -6,10 +6,10 @@ import ( "path/filepath" "testing" - "github.com/charmbracelet/wish" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish/testsession" "github.com/matryer/is" - "golang.org/x/crypto/ssh" + gossh "golang.org/x/crypto/ssh" ) func TestGetInfo(t *testing.T) { @@ -110,10 +110,10 @@ func TestInvalidOps(t *testing.T) { }) } -func setup(tb testing.TB, rh CopyToClientHandler, wh CopyFromClientHandler) *ssh.Session { +func setup(tb testing.TB, rh CopyToClientHandler, wh CopyFromClientHandler) *gossh.Session { tb.Helper() - return testsession.New(tb, &wish.Server{ - Handler: Middleware(rh, wh)(func(s wish.Session) { + return testsession.New(tb, &ssh.Server{ + Handler: Middleware(rh, wh)(func(s ssh.Session) { s.Exit(0) s.Close() }), diff --git a/types.go b/types.go deleted file mode 100644 index 4568ea1..0000000 --- a/types.go +++ /dev/null @@ -1,73 +0,0 @@ -package wish - -import ( - "context" - "sync" - - "github.com/charmbracelet/ssh" - gossh "golang.org/x/crypto/ssh" -) - -// Option is a functional option handler for Server. -type Option = ssh.Option - -// Handler is a callback for handling established SSH sessions. -type Handler = ssh.Handler - -// PublicKey is an abstraction of different types of public keys. -type PublicKey = ssh.PublicKey - -// The Permissions type holds fine-grained permissions that are specific to a -// user or a specific authentication method for a user. Permissions, except for -// "source-address", must be enforced in the server application layer, after -// successful authentication. -type Permissions = ssh.Permissions - -// A Signer can create signatures that verify against a public key. -type Signer = ssh.Signer - -// PublicKeyHandler is a callback for performing public key authentication. -type PublicKeyHandler func(ctx Context, key PublicKey) bool - -// PasswordHandler is a callback for performing password authentication. -type PasswordHandler func(ctx Context, password string) bool - -// KeyboardInteractiveHandler is a callback for performing keyboard-interactive authentication. -type KeyboardInteractiveHandler func(ctx Context, challenger gossh.KeyboardInteractiveChallenge) bool - -// Context is a package specific context interface. It exposes connection -// metadata and allows new values to be easily written to it. It's used in -// authentication handlers and callbacks, and its underlying context.Context is -// exposed on Session in the session Handler. A connection-scoped lock is also -// embedded in the context to make it easier to limit operations per-connection. -type Context interface { - context.Context - sync.Locker - ssh.Context -} - -// Server defines parameters for running an SSH server. The zero value for -// Server is a valid configuration. When both PasswordHandler and -// PublicKeyHandler are nil, no client authentication is performed. -type Server = ssh.Server - -// Session provides access to information about an SSH session and methods -// to read and write to the SSH channel with an embedded Channel interface from -// crypto/ssh. -// -// When Command() returns an empty slice, the user requested a shell. Otherwise -// the user is performing an exec with those command arguments. -type Session = ssh.Session - -var ( - // HostKeyFile returns a functional option that adds HostSigners to the server - // from a PEM file at filepath. - HostKeyFile = ssh.HostKeyFile - - // HostKeyPEM returns a functional option that adds HostSigners to the server - // from a PEM file as bytes. - HostKeyPEM = ssh.HostKeyPEM - - // KeysEqual is constant time compare of the keys to avoid timing attacks. - KeysEqual = ssh.KeysEqual -) diff --git a/wish.go b/wish.go index 79cc760..215936b 100644 --- a/wish.go +++ b/wish.go @@ -5,18 +5,19 @@ import ( "io" "github.com/charmbracelet/keygen" + "github.com/charmbracelet/ssh" ) // Middleware is a function that takes an ssh.Handler and returns an // ssh.Handler. Implementations should call the provided handler argument. -type Middleware func(Handler) Handler +type Middleware func(ssh.Handler) ssh.Handler // NewServer is returns a default SSH server with the provided Middleware. A // new SSH key pair of type ed25519 will be created if one does not exist. By // default this server will accept all incoming connections, password and // public key. -func NewServer(ops ...Option) (*Server, error) { - s := &Server{} +func NewServer(ops ...ssh.Option) (*ssh.Server, error) { + s := &ssh.Server{} // Some sensible defaults s.Version = "OpenSSH_7.6p1" for _, op := range ops { @@ -38,7 +39,7 @@ func NewServer(ops ...Option) (*Server, error) { } // Fatal prints to the given session's STDERR and exits 1. -func Fatal(s Session, v ...interface{}) { +func Fatal(s ssh.Session, v ...interface{}) { Error(s, v...) _ = s.Exit(1) _ = s.Close() @@ -48,7 +49,7 @@ func Fatal(s Session, v ...interface{}) { // followed by an exit 1. // // Notice that this might cause formatting issues if you don't add a \r\n in the end of your string. -func Fatalf(s Session, f string, v ...interface{}) { +func Fatalf(s ssh.Session, f string, v ...interface{}) { Errorf(s, f, v...) _ = s.Exit(1) _ = s.Close() @@ -56,7 +57,7 @@ func Fatalf(s Session, f string, v ...interface{}) { // Fatalln formats according to the default format, prints to the session's // STDERR, followed by a new line and an exit 1. -func Fatalln(s Session, v ...interface{}) { +func Fatalln(s ssh.Session, v ...interface{}) { Errorln(s, v...) Errorf(s, "\r") _ = s.Exit(1) @@ -64,36 +65,36 @@ func Fatalln(s Session, v ...interface{}) { } // Error prints the given error the the session's STDERR. -func Error(s Session, v ...interface{}) { +func Error(s ssh.Session, v ...interface{}) { _, _ = fmt.Fprint(s.Stderr(), v...) } // Errorf formats according to the given format and prints to the session's STDERR. -func Errorf(s Session, f string, v ...interface{}) { +func Errorf(s ssh.Session, f string, v ...interface{}) { _, _ = fmt.Fprintf(s.Stderr(), f, v...) } // Errorf formats according to the default format and prints to the session's STDERR. -func Errorln(s Session, v ...interface{}) { +func Errorln(s ssh.Session, v ...interface{}) { _, _ = fmt.Fprintln(s.Stderr(), v...) } // Print writes to the session's STDOUT followed. -func Print(s Session, v ...interface{}) { +func Print(s ssh.Session, v ...interface{}) { _, _ = fmt.Fprint(s, v...) } // Printf formats according to the given format and writes to the session's STDOUT. -func Printf(s Session, f string, v ...interface{}) { +func Printf(s ssh.Session, f string, v ...interface{}) { _, _ = fmt.Fprintf(s, f, v...) } // Println formats according to the default format and writes to the session's STDOUT. -func Println(s Session, v ...interface{}) { +func Println(s ssh.Session, v ...interface{}) { _, _ = fmt.Fprintln(s, v...) } // WriteString writes the given string to the session's STDOUT. -func WriteString(s Session, v string) (int, error) { +func WriteString(s ssh.Session, v string) (int, error) { return io.WriteString(s, v) } diff --git a/wish_test.go b/wish_test.go index 0a651b2..b5ec113 100755 --- a/wish_test.go +++ b/wish_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish/testsession" ) @@ -30,8 +31,8 @@ func TestNewServerWithOptions(t *testing.T) { func TestError(t *testing.T) { eerr := errors.New("foo err") - sess := testsession.New(t, &Server{ - Handler: func(s Session) { + sess := testsession.New(t, &ssh.Server{ + Handler: func(s ssh.Session) { Error(s, eerr) }, }, nil) @@ -47,8 +48,8 @@ func TestError(t *testing.T) { func TestFatal(t *testing.T) { err := errors.New("foo err") - sess := testsession.New(t, &Server{ - Handler: func(s Session) { + sess := testsession.New(t, &ssh.Server{ + Handler: func(s ssh.Session) { Fatal(s, err) }, }, nil)