Abstracted sshd.Connection; Op works now.
authorAndrey Petrov <andrey.petrov@shazow.net>
Sat, 10 Jan 2015 21:46:36 +0000 (13:46 -0800)
committerAndrey Petrov <andrey.petrov@shazow.net>
Sat, 10 Jan 2015 21:46:36 +0000 (13:46 -0800)
auth.go
auth_test.go
chat/channel.go
chat/channel_test.go
host.go
sshd/client_test.go
sshd/terminal.go

diff --git a/auth.go b/auth.go
index 4d6de86aa0a373825d6901afbbf5626b0d041b88..d218366c4a3c32b3303a9478b7a9675c6cc21038 100644 (file)
--- a/auth.go
+++ b/auth.go
@@ -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
+}
index cb7e521266dbda0a71f518259cc901760bdfc556..3b1121236f2883164f787b3dc14405fff7a5a7d7 100644 (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)
        }
index 4dadbae2624bdb23333f0448c67dfb1f4ccff15a..34d981f3040eb7666714cba57a3bbe8e4ca41b61 100644 (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.
index 9ba73d847b4c2ac09d620371669e600a47d09fdf..13c9bdcbd2b79b6b21daf35fb13162f34901cf5f 100644 (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)
        }
diff --git a/host.go b/host.go
index 376c9660781c6b4fe7209499d6b10b9c17c47689..51a1fb58ae120cbf7765f5fcf8e0695dc70b2ff7 100644 (file)
--- a/host.go
+++ b/host.go
@@ -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 {
index 2fd109fab25983e0ce995f8124e45ef6e9c0d8ca..b115e8fb8c1ea023890c5a650194549d7e4323a4 100644 (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) {
index 196b9bea1737357d128965d5da0075d678f58979..bfd16a044011929a67cbe14114f2b0c66d3c01cd 100644 (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,
        }