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()
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)
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
+}
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)
}
}
// 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.
go ch.Serve()
defer ch.Close()
- err := ch.Join(u)
+ _, err := ch.Join(u)
if err != nil {
t.Fatal(err)
}
ch := NewChannel()
defer ch.Close()
- err := ch.Join(u)
+ _, err := ch.Join(u)
if err != nil {
t.Fatal(err)
}
ch := NewChannel()
defer ch.Close()
- err := ch.Join(u)
+ _, err := ch.Join(u)
if err != nil {
t.Fatal(err)
}
go ch.Serve()
defer ch.Close()
- err := ch.Join(u)
+ _, err := ch.Join(u)
if err != nil {
t.Fatal(err)
}
go ch.Serve()
defer ch.Close()
- err := ch.Join(u)
+ _, err := ch.Join(u)
if err != nil {
t.Fatal(err)
}
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)
}()
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)
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 {
}
func consume(ch <-chan *Terminal) {
- for range ch {}
+ for _ = range ch {
+ }
}
func TestClientReject(t *testing.T) {
"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
}
}
term := Terminal{
*terminal.NewTerminal(channel, "Connecting..."),
- conn,
+ sshConn{conn},
channel,
}