Abstracted sshd.Connection; Op works now.

This commit is contained in:
Andrey Petrov 2015-01-10 13:46:36 -08:00
parent d8d5deac1c
commit d5626b7514
7 changed files with 93 additions and 20 deletions

29
auth.go
View File

@ -81,7 +81,16 @@ func (a *Auth) Op(key ssh.PublicKey) {
a.Unlock()
}
// Whitelist will set a fingerprint as a whitelisted user.
// IsOp checks if a public key is an op.
func (a Auth) IsOp(key ssh.PublicKey) bool {
authkey := NewAuthKey(key)
a.RLock()
_, ok := a.ops[authkey]
a.RUnlock()
return ok
}
// Whitelist will set a public key as a whitelisted user.
func (a *Auth) Whitelist(key ssh.PublicKey) {
authkey := NewAuthKey(key)
a.Lock()
@ -89,6 +98,15 @@ func (a *Auth) Whitelist(key ssh.PublicKey) {
a.Unlock()
}
// IsWhitelisted checks if a public key is whitelisted.
func (a Auth) IsWhitelisted(key ssh.PublicKey) bool {
authkey := NewAuthKey(key)
a.RLock()
_, ok := a.whitelist[authkey]
a.RUnlock()
return ok
}
// Ban will set a fingerprint as banned.
func (a *Auth) Ban(key ssh.PublicKey) {
authkey := NewAuthKey(key)
@ -96,3 +114,12 @@ func (a *Auth) Ban(key ssh.PublicKey) {
a.banned[authkey] = struct{}{}
a.Unlock()
}
// IsBanned will set a fingerprint as banned.
func (a Auth) IsBanned(key ssh.PublicKey) bool {
authkey := NewAuthKey(key)
a.RLock()
_, ok := a.whitelist[authkey]
a.RUnlock()
return ok
}

View File

@ -35,16 +35,16 @@ func TestAuthWhitelist(t *testing.T) {
auth.Whitelist(key)
key_clone, err := ClonePublicKey(key)
keyClone, err := ClonePublicKey(key)
if err != nil {
t.Fatal(err)
}
if string(key_clone.Marshal()) != string(key.Marshal()) {
if string(keyClone.Marshal()) != string(key.Marshal()) {
t.Error("Clone key does not match.")
}
ok, err = auth.Check(key_clone)
ok, err = auth.Check(keyClone)
if !ok || err != nil {
t.Error("Failed to permit whitelisted:", err)
}

View File

@ -114,17 +114,18 @@ func (ch *Channel) Send(m Message) {
}
// Join the channel as a user, will announce.
func (ch *Channel) Join(u *User) error {
func (ch *Channel) Join(u *User) (*Member, error) {
if ch.closed {
return ErrChannelClosed
return nil, ErrChannelClosed
}
err := ch.members.Add(&Member{u, false})
member := Member{u, false}
err := ch.members.Add(&member)
if err != nil {
return err
return nil, err
}
s := fmt.Sprintf("%s joined. (Connected: %d)", u.Name(), ch.members.Len())
ch.Send(NewAnnounceMsg(s))
return nil
return &member, nil
}
// Leave the channel as a user, will announce. Mostly used during setup.

View File

@ -28,7 +28,7 @@ func TestChannelJoin(t *testing.T) {
go ch.Serve()
defer ch.Close()
err := ch.Join(u)
_, err := ch.Join(u)
if err != nil {
t.Fatal(err)
}
@ -66,7 +66,7 @@ func TestChannelDoesntBroadcastAnnounceMessagesWhenQuiet(t *testing.T) {
ch := NewChannel()
defer ch.Close()
err := ch.Join(u)
_, err := ch.Join(u)
if err != nil {
t.Fatal(err)
}
@ -101,7 +101,7 @@ func TestChannelQuietToggleBroadcasts(t *testing.T) {
ch := NewChannel()
defer ch.Close()
err := ch.Join(u)
_, err := ch.Join(u)
if err != nil {
t.Fatal(err)
}
@ -138,7 +138,7 @@ func TestQuietToggleDisplayState(t *testing.T) {
go ch.Serve()
defer ch.Close()
err := ch.Join(u)
_, err := ch.Join(u)
if err != nil {
t.Fatal(err)
}
@ -174,7 +174,7 @@ func TestChannelNames(t *testing.T) {
go ch.Serve()
defer ch.Close()
err := ch.Join(u)
_, err := ch.Join(u)
if err != nil {
t.Fatal(err)
}

17
host.go
View File

@ -46,9 +46,17 @@ func (h *Host) SetMotd(motd string) {
h.motd = motd
}
func (h Host) isOp(conn sshd.Connection) bool {
key, ok := conn.PublicKey()
if !ok {
return false
}
return h.auth.IsOp(key)
}
// Connect a specific Terminal to this host and its channel.
func (h *Host) Connect(term *sshd.Terminal) {
name := term.Conn.User()
name := term.Conn.Name()
term.AutoCompleteCallback = h.AutoCompleteFunction
user := chat.NewUserScreen(name, term)
@ -60,11 +68,11 @@ func (h *Host) Connect(term *sshd.Terminal) {
}()
defer user.Close()
err := h.channel.Join(user)
member, err := h.channel.Join(user)
if err == chat.ErrIdTaken {
// Try again...
user.SetName(fmt.Sprintf("Guest%d", h.count))
err = h.channel.Join(user)
member, err = h.channel.Join(user)
}
if err != nil {
logger.Errorf("Failed to join: %s", err)
@ -75,6 +83,9 @@ func (h *Host) Connect(term *sshd.Terminal) {
term.SetPrompt(GetPrompt(user))
h.count++
// Should the user be op'd?
member.Op = h.isOp(term.Conn)
for {
line, err := term.ReadLine()
if err == io.EOF {

View File

@ -19,7 +19,8 @@ func (a RejectAuth) Check(ssh.PublicKey) (bool, error) {
}
func consume(ch <-chan *Terminal) {
for range ch {}
for _ = range ch {
}
}
func TestClientReject(t *testing.T) {

View File

@ -8,10 +8,43 @@ import (
"golang.org/x/crypto/ssh/terminal"
)
// Connection is an interface with fields necessary to operate an sshd host.
type Connection interface {
PublicKey() (ssh.PublicKey, bool)
Name() string
Close() error
}
type sshConn struct {
*ssh.ServerConn
}
func (c sshConn) PublicKey() (ssh.PublicKey, bool) {
if c.Permissions == nil {
return nil, false
}
s, ok := c.Permissions.Extensions["pubkey"]
if !ok {
return nil, false
}
key, err := ssh.ParsePublicKey([]byte(s))
if err != nil {
return nil, false
}
return key, true
}
func (c sshConn) Name() string {
return c.User()
}
// Extending ssh/terminal to include a closer interface
type Terminal struct {
terminal.Terminal
Conn *ssh.ServerConn
Conn Connection
Channel ssh.Channel
}
@ -26,7 +59,7 @@ func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) {
}
term := Terminal{
*terminal.NewTerminal(channel, "Connecting..."),
conn,
sshConn{conn},
channel,
}