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