Fix: GetUser, Redo bans

This commit is contained in:
kayos@tcp.direct 2021-09-29 07:33:22 -07:00
parent 1f4cefd972
commit bac6cae764
15 changed files with 185 additions and 71 deletions

View File

@ -216,36 +216,41 @@ func (users *UserDB) Sync(user *RegisteredUser) error {
}
// GetUser iterates through all RegisteredUser instances in the database and returns a pointer to the one that matches the requested username.
func (users *UserDB) GetUser(user string) (usr *RegisteredUser, err error) {
func (users *UserDB) GetUser(targetUser string) (*RegisteredUser, error) {
var (
usrbytes []byte
usrbytes []byte
err error
userStruct = &RegisteredUser{}
)
if users.DB.Len() < 1 {
if users.DB.Len() <= 0 {
return &RegisteredUser{}, errors.New("no users in database")
}
ukeys := users.DB.Keys()
for {
key, ok := <-ukeys
if !ok {
break
}
err = users.DB.Fold(func(key []byte) error {
if usrbytes, err = users.DB.Get(key); err != nil {
continue
log.Fatal().Err(err).Bytes("key", key).Msg("failed to get userbytes")
// continue
}
if err := json.Unmarshal(usrbytes, &usr); err != nil {
log.Error().Err(err).Msg("failed to unmarshal")
continue
if err := json.Unmarshal(usrbytes, &userStruct); err != nil {
log.Fatal().Err(err).Msg("failed to unmarshal")
// continue
}
if usr.Username == user {
return usr, nil
if userStruct.Username == targetUser {
return errors.New("done")
}
return nil
})
if err != nil {
if err.Error() == "done" {
return userStruct, nil
}
}
return nil, errors.New("user does not exist: " + user)
return &RegisteredUser{}, errors.New("targetUser does not exist: " + targetUser)
}
// KeyExists iterates through all RegisteredUser instances in the database and returns the corresponding RegisteredUser and true if a public key is present.

View File

@ -33,26 +33,40 @@ type BanList struct {
}
func init() {
banCache = cache.New(2*time.Hour, 10*time.Minute)
banCache = cache.New(2*time.Hour, 25*time.Minute)
}
func parseBanQuery(s string) (string, BanType, error) {
query := strings.TrimSpace(s)
request := strings.Split(query, "=")
bantype, err := StringToBanType(request[1])
if err != nil {
return "", 100, errors.New("unknown key")
log.Debug().Err(err).Str("caller", s).
Str("request[0]", request[0]).
Str("request[1]", request[1]).Msg("unknown key")
}
return request[1], bantype, nil
}
// BanQuery is used to ingest strings that contain various types of bans seperated by an equals sign.
// e.g: /ban name=douchebag
func (users *UserDB) BanQuery(query string) (err error) {
request := strings.Split(query, "=")
switch strings.ToLower(request[0]) {
case "client":
err = users.BanOther(request[1], Client)
case "name":
err = users.BanOther(request[1], Name)
case "ip":
err = users.BanOther(request[1], IP)
case "key":
err = users.BanOther(request[1], IP)
default:
return errors.New("unknown key")
target, bantype, err := parseBanQuery(query)
if err != nil {
return err
}
return
return users.BanOther(target, bantype, true)
}
// UnBanQuery is used to ingest strings that contain various types of bans seperated by an equals sign.
// e.g: /unban name=knucklehead
func (users *UserDB) UnBanQuery(query string) (err error) {
target, bantype, err := parseBanQuery(query)
if err != nil {
return err
}
return users.BanOther(target, bantype, false)
}
// Banned returns the current banned items list.
@ -84,6 +98,8 @@ func (users *UserDB) banned(bantype BanType) []string {
// CheckBans checks our ban list for the instance of any of the given elements.
func (users *UserDB) CheckBans(user string, addr net.Addr, key ssh.PublicKey, s string) error {
log.Debug().Caller().Msg("checkbans")
var (
userRet = errors.New("user is banned")
addrRet = errors.New("ip is banned")
@ -96,52 +112,68 @@ func (users *UserDB) CheckBans(user string, addr net.Addr, key ssh.PublicKey, s
return
}
clientAddr, _, err := net.SplitHostPort(addr.String())
if err != nil {
panic(err)
}
// check cache before we check the database
switch {
case present(user):
return userRet
case present(addr.String()):
case present(clientAddr):
return addrRet
case key != nil:
case present(s):
return clientRet
default:
if key == nil {
break
}
if present(string(key.Marshal())) {
return keyRet
}
case present(s):
return clientRet
}
// check database, if found add to cache
if users.CheckBanOther(addr.String(), IP) {
banCache.Add(addr.String(), true, cache.DefaultExpiration)
return addrRet
}
if users.CheckBanOther(s, Client) {
banCache.Add(s, true, cache.DefaultExpiration)
return clientRet
}
if users.CheckBanOther(user, Name) {
banCache.Add(user, true, cache.DefaultExpiration)
return userRet
var toCheck = make(map[string]BanType)
toCheck[addr.String()] = IP
toCheck[s] = Client
toCheck[user] = Name
for item, bt := range toCheck {
if users.CheckBanOther(item, bt) {
if err := banCache.Add(addr.String(), true, cache.DefaultExpiration); err != nil {
log.Debug().Caller().Err(err).Msg("ban cache failure")
}
switch bt {
case IP:
return addrRet
case Client:
return clientRet
case Name:
return userRet
default:
log.Warn().Caller().Msg("unhandled ban")
}
}
}
if key == nil {
return nil
}
if users.CheckBanOther(string(key.Marshal()), Key) {
banCache.Add(string(key.Marshal()), true, cache.DefaultExpiration)
if err := banCache.Add(string(key.Marshal()), true, cache.DefaultExpiration); err != nil {
log.Debug().Caller().Err(err).Msg("ban cache failure")
}
return keyRet
}
return nil
}
// BanOther creates a ban on various types of client attributes.
func (users *UserDB) BanOther(target string, bantype BanType) error {
// BanOther creates a ban on various types of client attributes, or unbans them if banunban is false.
func (users *UserDB) BanOther(target string, bantype BanType, banunban bool) error {
bans := uint32ToBytes(uint32(bantype))
bad := &BanList{Items: []string{}}
defer func() {
print("yeet")
}()
if users.DB.Has(bans) {
badBytes, err := users.DB.Get(bans)
if err != nil {
@ -158,13 +190,43 @@ func (users *UserDB) BanOther(target string, bantype BanType) error {
}
}
bad.Items = append(bad.Items, target)
newbads, err := json.Marshal(bad)
var newbans []string
if banunban {
bad.Items = append(bad.Items, target)
} else {
for _, item := range bad.Items {
if item != target {
newbans = append(bad.Items, item)
}
}
if len(bad.Items) == len(newbans) {
return errors.New("ban does not exist")
}
bad.Items = newbans
}
newitems, err := json.Marshal(bad)
if err != nil {
return err
}
return users.DB.Put(bans, newitems)
}
return users.DB.Put(bans, newbads)
// StringToBanType takes a string and returns the corresponding BanType if found.
func StringToBanType(banstr string) (BanType, error) {
switch banstr {
case "name":
return Name, nil
case "ip":
return IP, nil
case "client":
return Client, nil
case "key":
return Key, nil
default:
return 100, errors.New("ban type not found")
}
}
// CheckBanOther checks a given target of bantype against our banned items.
@ -215,3 +277,16 @@ func (users *UserDB) Ban(username string) error {
}
return nil
}
// UnBan unbans a user from accessing their account and resets their privileges.
// If they were previously opped you will need to re-op them.
func (users *UserDB) UnBan(username string) error {
user, err := users.GetUser(username)
if err != nil {
return err
}
if err := users.SetPrivLevel(user, LevelBanned); err != nil {
return err
}
return nil
}

View File

@ -79,8 +79,8 @@ func (c Commands) Run(room *Room, msg message.CommandMsg) error {
// Help will return collated help text as one string.
func (c Commands) Help(showOp bool) string {
// Filter by op
op := []*Command{}
normal := []*Command{}
var op []*Command
var normal []*Command
for _, cmd := range c {
if cmd.Op {
op = append(op, cmd)

View File

@ -201,7 +201,7 @@ var DefaultTheme *Theme
var MonoTheme *Theme
func allColors256() *Palette {
colors := []uint8{}
var colors []uint8
var i uint8
for i = 0; i < 255; i++ {
colors = append(colors, i)
@ -210,7 +210,7 @@ func allColors256() *Palette {
}
func readableColors256() *Palette {
colors := []uint8{}
var colors []uint8
var i uint8
for i = 0; i < 255; i++ {
if _, ok := badColors[i]; ok {

View File

@ -11,7 +11,6 @@ import (
"git.tcp.direct/kayos/sh3lly/set"
)
const historyLen = 20
const roomBuffer = 10
var ExternalAnnouncements chan *message.AnnounceMsg
@ -192,6 +191,7 @@ func (r *Room) Join(u *message.User) (*Member, error) {
return nil, err
}
// TODO: Remove user ID from sets, probably referring to a prior user.
s := fmt.Sprintf("%s joined. (Connected: %d)", u.Name(), r.Members.Len())
r.Send(message.NewAnnounceMsg(s))

View File

@ -60,8 +60,8 @@ type Host struct {
// NewHost creates a Host on top of an existing listener.
func NewHost(listener *sshd.SSHListener, auth *auth.UserDB) *Host {
room := chat.NewRoom()
h := &Host{
Room: room,
listener: listener,
@ -149,6 +149,7 @@ func (h *Host) Connect(term *sshd.Terminal) {
member, err := h.Join(user)
if err != nil {
id.SetName(fmt.Sprintf("%s%d", id.Name(), count))
member, err = h.Join(user)
}
if err != nil {
@ -355,6 +356,7 @@ func (h *Host) GetUser(name string) (*message.User, bool) {
// InitCommands adds host-specific commands to a Commands container. These will
// override any existing commands.
//goland:noinspection GoUnhandledErrorResult
func (h *Host) InitCommands(c *chat.Commands) {
sendPM := func(room *chat.Room, msg string, from *message.User, target *message.User) error {
m := message.NewPrivateMsg(msg, from, target)
@ -537,6 +539,45 @@ func (h *Host) InitCommands(c *chat.Commands) {
},
})
c.Add(chat.Command{
Op: true,
Prefix: "/unban",
PrefixHelp: "QUERY",
Help: "Unban from the server. QUERY needs to match your the query used when /ban was used.",
Handler: func(room *chat.Room, msg message.CommandMsg) error {
if !room.IsOp(msg.From()) {
return errors.New("must be op")
}
args := msg.Args()
if len(args) == 0 {
return errors.New("must specify user")
}
query := args[0]
target, ok := h.GetUser(query)
if !ok {
query = strings.Join(args, " ")
if strings.Contains(query, "=") {
return h.auth.UnBanQuery(query)
}
return errors.New("user not found")
}
id := target.Identifier.ID()
if err := h.auth.UnBan(id); err != nil {
return err
}
body := fmt.Sprintf("%s was unbanned by %s.", target.Name(), msg.From().Name())
room.Send(message.NewAnnounceMsg(body))
target.Close()
log.Debug().Msgf("UnbanList: \n-> %s", id)
return nil
},
})
c.Add(chat.Command{
Op: true,
Prefix: "/banned",

View File

@ -35,13 +35,6 @@ func (item StringItem) Value() interface{} {
return true
}
func Expire(item Item, d time.Duration) Item {
return &ExpiringItem{
Item: item,
Time: time.Now().Add(d),
}
}
type ExpiringItem struct {
Item
time.Time

View File

@ -197,7 +197,7 @@ func (s *Set) Each(fn IterFunc) error {
// ListPrefix returns a list of items with a prefix, normalized.
// TODO: Add trie for efficient prefix lookup
func (s *Set) ListPrefix(prefix string) []Item {
r := []Item{}
var r []Item
prefix = s.normalize(prefix)
s.Each(func(key string, item Item) error {