fix bug with user perms not being applied independantly

This commit is contained in:
Liam Stanley 2017-08-08 05:33:15 -04:00
parent 2b52b21176
commit b7e0086816
3 changed files with 83 additions and 18 deletions

View File

@ -485,7 +485,9 @@ func handleNAMES(c *Client, e Event) {
} }
// Don't append modes, overwrite them. // Don't append modes, overwrite them.
user.Perms.set(modes, false) perms, _ := user.Perms.Lookup(channel.Name)
perms.set(modes, false)
user.Perms.set(channel.Name, perms)
} }
c.state.Unlock() c.state.Unlock()
c.state.notify(c, UPDATE_STATE) c.state.notify(c, UPDATE_STATE)

View File

@ -4,7 +4,11 @@
package girc package girc
import "strings" import (
"encoding/json"
"strings"
"sync"
)
// CMode represents a single step of a given mode change. // CMode represents a single step of a given mode change.
type CMode struct { type CMode struct {
@ -353,7 +357,9 @@ func handleMODE(c *Client, e Event) {
user := c.state.lookupUser(modes[i].args) user := c.state.lookupUser(modes[i].args)
if user != nil { if user != nil {
user.Perms.setFromMode(modes[i]) perms, _ := user.Perms.Lookup(channel.Name)
perms.setFromMode(modes[i])
user.Perms.set(channel.Name, perms)
} }
} }
@ -382,30 +388,83 @@ func (s *state) userPrefixes() string {
return DefaultPrefixes return DefaultPrefixes
} }
// UserPerms contains all channel-based user permissions. The minimum op, and // UserPerms contains all of the permissions for each channel the user is
// in.
type UserPerms struct {
mu sync.RWMutex
channels map[string]Perms
}
// Copy returns a deep copy of the channel permissions.
func (p *UserPerms) Copy() (perms *UserPerms) {
np := &UserPerms{
channels: make(map[string]Perms),
}
p.mu.RLock()
for key := range p.channels {
np.channels[key] = p.channels[key]
}
p.mu.RUnlock()
return np
}
// MarshalJSON implements json.Marshaler.
func (p *UserPerms) MarshalJSON() ([]byte, error) {
p.mu.Lock()
out, err := json.Marshal(&p.channels)
p.mu.Unlock()
return out, err
}
// Lookup looks up the users permissions for a given channel. ok is false
// if the user is not in the given channel.
func (p *UserPerms) Lookup(channel string) (perms Perms, ok bool) {
p.mu.RLock()
perms, ok = p.channels[ToRFC1459(channel)]
p.mu.RUnlock()
return perms, ok
}
func (p *UserPerms) set(channel string, perms Perms) {
p.mu.Lock()
p.channels[ToRFC1459(channel)] = perms
p.mu.Unlock()
}
func (p *UserPerms) remove(channel string) {
p.mu.Lock()
delete(p.channels, ToRFC1459(channel))
p.mu.Unlock()
}
// Perms contains all channel-based user permissions. The minimum op, and
// voice should be supported on all networks. This also supports non-rfc // voice should be supported on all networks. This also supports non-rfc
// Owner, Admin, and HalfOp, if the network has support for it. // Owner, Admin, and HalfOp, if the network has support for it.
type UserPerms struct { type Perms struct {
// Owner (non-rfc) indicates that the user has full permissions to the // Owner (non-rfc) indicates that the user has full permissions to the
// channel. More than one user can have owner permission. // channel. More than one user can have owner permission.
Owner bool Owner bool `json:"owner"`
// Admin (non-rfc) is commonly given to users that are trusted enough // Admin (non-rfc) is commonly given to users that are trusted enough
// to manage channel permissions, as well as higher level service settings. // to manage channel permissions, as well as higher level service settings.
Admin bool Admin bool `json:"admin"`
// Op is commonly given to trusted users who can manage a given channel // Op is commonly given to trusted users who can manage a given channel
// by kicking, and banning users. // by kicking, and banning users.
Op bool Op bool `json:"op"`
// HalfOp (non-rfc) is commonly used to give users permissions like the // HalfOp (non-rfc) is commonly used to give users permissions like the
// ability to kick, without giving them greater abilities to ban all users. // ability to kick, without giving them greater abilities to ban all users.
HalfOp bool HalfOp bool `json:"half_op"`
// Voice indicates the user has voice permissions, commonly given to known // Voice indicates the user has voice permissions, commonly given to known
// users, with very light trust, or to indicate a user is active. // users, with very light trust, or to indicate a user is active.
Voice bool Voice bool `json:"voice"`
} }
// IsAdmin indicates that the user has banning abilities, and are likely a // IsAdmin indicates that the user has banning abilities, and are likely a
// very trustable user (e.g. op+). // very trustable user (e.g. op+).
func (m UserPerms) IsAdmin() bool { func (m Perms) IsAdmin() bool {
if m.Owner || m.Admin || m.Op { if m.Owner || m.Admin || m.Op {
return true return true
} }
@ -415,7 +474,7 @@ func (m UserPerms) IsAdmin() bool {
// IsTrusted indicates that the user at least has modes set upon them, higher // IsTrusted indicates that the user at least has modes set upon them, higher
// than a regular joining user. // than a regular joining user.
func (m UserPerms) IsTrusted() bool { func (m Perms) IsTrusted() bool {
if m.IsAdmin() || m.HalfOp || m.Voice { if m.IsAdmin() || m.HalfOp || m.Voice {
return true return true
} }
@ -424,7 +483,7 @@ func (m UserPerms) IsTrusted() bool {
} }
// reset resets the modes of a user. // reset resets the modes of a user.
func (m *UserPerms) reset() { func (m *Perms) reset() {
m.Owner = false m.Owner = false
m.Admin = false m.Admin = false
m.Op = false m.Op = false
@ -434,7 +493,7 @@ func (m *UserPerms) reset() {
// set translates raw prefix characters into proper permissions. Only // set translates raw prefix characters into proper permissions. Only
// use this function when you have a session lock. // use this function when you have a session lock.
func (m *UserPerms) set(prefix string, append bool) { func (m *Perms) set(prefix string, append bool) {
if !append { if !append {
m.reset() m.reset()
} }
@ -457,7 +516,7 @@ func (m *UserPerms) set(prefix string, append bool) {
// setFromMode sets user-permissions based on channel user mode chars. E.g. // setFromMode sets user-permissions based on channel user mode chars. E.g.
// "o" being oper, "v" being voice, etc. // "o" being oper, "v" being voice, etc.
func (m *UserPerms) setFromMode(mode CMode) { func (m *Perms) setFromMode(mode CMode) {
switch string(mode.name) { switch string(mode.name) {
case ModeOwner: case ModeOwner:
m.Owner = mode.add m.Owner = mode.add

View File

@ -83,9 +83,7 @@ type User struct {
// Perms are the user permissions applied to this user that affect the given // Perms are the user permissions applied to this user that affect the given
// channel. This supports non-rfc style modes like Admin, Owner, and HalfOp. // channel. This supports non-rfc style modes like Admin, Owner, and HalfOp.
// If you want to easily check if a user has permissions equal or greater Perms *UserPerms
// than OP, use Perms.IsAdmin().
Perms UserPerms
// Extras are things added on by additional tracking methods, which may // Extras are things added on by additional tracking methods, which may
// or may not work on the IRC server in mention. // or may not work on the IRC server in mention.
@ -113,6 +111,7 @@ func (u *User) Copy() *User {
nu := &User{} nu := &User{}
*nu = *u *nu = *u
nu.Perms = u.Perms.Copy()
_ = copy(nu.Channels, u.Channels) _ = copy(nu.Channels, u.Channels)
return nu return nu
@ -122,6 +121,8 @@ func (u *User) Copy() *User {
func (u *User) addChannel(name string) { func (u *User) addChannel(name string) {
u.Channels = append(u.Channels, ToRFC1459(name)) u.Channels = append(u.Channels, ToRFC1459(name))
sort.StringsAreSorted(u.Channels) sort.StringsAreSorted(u.Channels)
u.Perms.set(name, Perms{})
} }
// deleteChannel removes an existing channel from the users channel list. // deleteChannel removes an existing channel from the users channel list.
@ -139,6 +140,8 @@ func (u *User) deleteChannel(name string) {
if j != -1 { if j != -1 {
u.Channels = append(u.Channels[:j], u.Channels[j+1:]...) u.Channels = append(u.Channels[:j], u.Channels[j+1:]...)
} }
u.Perms.remove(name)
} }
// InChannel checks to see if a user is in the given channel. // InChannel checks to see if a user is in the given channel.
@ -312,6 +315,7 @@ func (s *state) createUser(nick string) (ok bool) {
Nick: nick, Nick: nick,
FirstSeen: time.Now(), FirstSeen: time.Now(),
LastActive: time.Now(), LastActive: time.Now(),
Perms: &UserPerms{channels: make(map[string]Perms)},
} }
return true return true