mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-04-22 19:50:33 +03:00
parent
953c3d46b2
commit
60701198fc
12
chat/room.go
12
chat/room.go
@ -25,6 +25,7 @@ var ErrInvalidName = errors.New("invalid name")
|
||||
// Member is a User with per-Room metadata attached to it.
|
||||
type Member struct {
|
||||
*message.User
|
||||
IsOp bool
|
||||
}
|
||||
|
||||
// Room definition, also a Set of User Items
|
||||
@ -37,7 +38,6 @@ type Room struct {
|
||||
closeOnce sync.Once
|
||||
|
||||
Members *set.Set
|
||||
Ops *set.Set
|
||||
}
|
||||
|
||||
// NewRoom creates a new room.
|
||||
@ -50,7 +50,6 @@ func NewRoom() *Room {
|
||||
commands: *defaultCommands,
|
||||
|
||||
Members: set.New(),
|
||||
Ops: set.New(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +147,7 @@ func (r *Room) Join(u *message.User) (*Member, error) {
|
||||
if u.ID() == "" {
|
||||
return nil, ErrInvalidName
|
||||
}
|
||||
member := &Member{u}
|
||||
member := &Member{User: u}
|
||||
err := r.Members.Add(set.Itemize(u.ID(), member))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -165,7 +164,6 @@ func (r *Room) Leave(u *message.User) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Ops.Remove(u.ID())
|
||||
s := fmt.Sprintf("%s left. (Connected %s)", u.Name(), humantime.Since(u.Joined()))
|
||||
r.Send(message.NewAnnounceMsg(s))
|
||||
return nil
|
||||
@ -211,7 +209,11 @@ func (r *Room) MemberByID(id string) (*Member, bool) {
|
||||
|
||||
// IsOp returns whether a user is an operator in this room.
|
||||
func (r *Room) IsOp(u *message.User) bool {
|
||||
return r.Ops.In(u.ID())
|
||||
m, ok := r.Member(u)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return m.IsOp
|
||||
}
|
||||
|
||||
// Topic of the room.
|
||||
|
25
host.go
25
host.go
@ -13,7 +13,6 @@ import (
|
||||
"github.com/shazow/ssh-chat/chat"
|
||||
"github.com/shazow/ssh-chat/chat/message"
|
||||
"github.com/shazow/ssh-chat/internal/humantime"
|
||||
"github.com/shazow/ssh-chat/set"
|
||||
"github.com/shazow/ssh-chat/sshd"
|
||||
)
|
||||
|
||||
@ -131,7 +130,7 @@ func (h *Host) Connect(term *sshd.Terminal) {
|
||||
|
||||
// Should the user be op'd on join?
|
||||
if h.isOp(term.Conn) {
|
||||
h.Room.Ops.Add(set.Itemize(member.ID(), member))
|
||||
member.IsOp = true
|
||||
}
|
||||
ratelimit := rateio.NewSimpleLimiter(3, time.Second*3)
|
||||
|
||||
@ -523,8 +522,8 @@ func (h *Host) InitCommands(c *chat.Commands) {
|
||||
c.Add(chat.Command{
|
||||
Op: true,
|
||||
Prefix: "/op",
|
||||
PrefixHelp: "USER [DURATION]",
|
||||
Help: "Set USER as admin.",
|
||||
PrefixHelp: "USER [DURATION|remove]",
|
||||
Help: "Set USER as admin. Duration only applies to pubkey reconnects.",
|
||||
Handler: func(room *chat.Room, msg message.CommandMsg) error {
|
||||
if !room.IsOp(msg.From()) {
|
||||
return errors.New("must be op")
|
||||
@ -535,21 +534,33 @@ func (h *Host) InitCommands(c *chat.Commands) {
|
||||
return errors.New("must specify user")
|
||||
}
|
||||
|
||||
opValue := true
|
||||
var until time.Duration
|
||||
if len(args) > 1 {
|
||||
until, _ = time.ParseDuration(args[1])
|
||||
if args[1] == "remove" {
|
||||
// Expire instantly
|
||||
until = time.Duration(1)
|
||||
opValue = false
|
||||
} else {
|
||||
until, _ = time.ParseDuration(args[1])
|
||||
}
|
||||
}
|
||||
|
||||
member, ok := room.MemberByID(args[0])
|
||||
if !ok {
|
||||
return errors.New("user not found")
|
||||
}
|
||||
room.Ops.Add(set.Itemize(member.ID(), member))
|
||||
member.IsOp = opValue
|
||||
|
||||
id := member.Identifier.(*Identity)
|
||||
h.auth.Op(id.PublicKey(), until)
|
||||
|
||||
body := fmt.Sprintf("Made op by %s.", msg.From().Name())
|
||||
var body string
|
||||
if opValue {
|
||||
body = fmt.Sprintf("Made op by %s.", msg.From().Name())
|
||||
} else {
|
||||
body = fmt.Sprintf("Removed op by %s.", msg.From().Name())
|
||||
}
|
||||
room.Send(message.NewSystemMsg(body, member.User))
|
||||
|
||||
return nil
|
||||
|
35
host_test.go
35
host_test.go
@ -6,12 +6,10 @@ import (
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/shazow/ssh-chat/chat/message"
|
||||
"github.com/shazow/ssh-chat/set"
|
||||
"github.com/shazow/ssh-chat/sshd"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
@ -186,21 +184,43 @@ func TestHostKick(t *testing.T) {
|
||||
go host.Serve()
|
||||
|
||||
connected := make(chan struct{})
|
||||
kicked := make(chan struct{})
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
// First client
|
||||
err := sshd.ConnectShell(addr, "foo", func(r io.Reader, w io.WriteCloser) error {
|
||||
scanner := bufio.NewScanner(r)
|
||||
|
||||
// Consume the initial buffer
|
||||
scanner.Scan()
|
||||
|
||||
// Make op
|
||||
member, _ := host.Room.MemberByID("foo")
|
||||
if member == nil {
|
||||
return errors.New("failed to load MemberByID")
|
||||
}
|
||||
host.Room.Ops.Add(set.Itemize(member.ID(), member))
|
||||
member.IsOp = true
|
||||
|
||||
// Change nicks, make sure op sticks
|
||||
w.Write([]byte("/nick quux\r\n"))
|
||||
scanner.Scan() // Prompt
|
||||
scanner.Scan() // Nick change response
|
||||
|
||||
// Block until second client is here
|
||||
connected <- struct{}{}
|
||||
scanner.Scan() // Connected message
|
||||
|
||||
w.Write([]byte("/kick bar\r\n"))
|
||||
scanner.Scan() // Prompt
|
||||
|
||||
scanner.Scan()
|
||||
if actual, expected := stripPrompt(scanner.Text()), " * bar was kicked by quux.\r"; actual != expected {
|
||||
t.Errorf("Got %q; expected %q", actual, expected)
|
||||
}
|
||||
|
||||
kicked <- struct{}{}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
@ -213,11 +233,14 @@ func TestHostKick(t *testing.T) {
|
||||
go func() {
|
||||
// Second client
|
||||
err := sshd.ConnectShell(addr, "bar", func(r io.Reader, w io.WriteCloser) error {
|
||||
scanner := bufio.NewScanner(r)
|
||||
<-connected
|
||||
scanner.Scan()
|
||||
|
||||
// Consume while we're connected. Should break when kicked.
|
||||
ioutil.ReadAll(r)
|
||||
return nil
|
||||
<-kicked
|
||||
|
||||
scanner.Scan()
|
||||
return scanner.Err()
|
||||
})
|
||||
if err != nil {
|
||||
close(done)
|
||||
|
Loading…
x
Reference in New Issue
Block a user