/op: Fix room op being tied to the nick, add remove option

Closes #301
This commit is contained in:
Andrey Petrov 2019-03-15 18:18:20 -04:00
parent 953c3d46b2
commit 60701198fc
3 changed files with 54 additions and 18 deletions

View File

@ -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
View File

@ -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

View File

@ -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)