changes to nick reservation

* Clients are now renamed, not disconnected, on reservation timeout
* Nick reservation config is now its own subsection
This commit is contained in:
Shivaram Lingamneni 2018-02-18 04:46:14 -05:00
parent 73391f11a6
commit 988cb22692
7 changed files with 88 additions and 58 deletions

View File

@ -51,7 +51,7 @@ func NewAccountManager(server *Server) *AccountManager {
}
func (am *AccountManager) buildNickToAccountIndex() {
if am.server.AccountConfig().NickReservation == NickReservationDisabled {
if am.server.AccountConfig().NickReservation.Enabled {
return
}
@ -98,6 +98,12 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
return errAccountCreation
}
// can't register a guest nickname
renamePrefix := strings.ToLower(am.server.AccountConfig().NickReservation.RenamePrefix)
if renamePrefix != "" && strings.HasPrefix(casefoldedAccount, renamePrefix) {
return errAccountAlreadyRegistered
}
accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)

View File

@ -99,9 +99,10 @@ func (clients *ClientManager) SetNick(client *Client, newNick string) error {
}
var reservedAccount string
reservation := client.server.AccountConfig().NickReservation
if reservation != NickReservationDisabled {
var method NickReservationMethod
if client.server.AccountConfig().NickReservation.Enabled {
reservedAccount = client.server.accounts.NickToAccount(newcfnick)
method = client.server.AccountConfig().NickReservation.Method
}
clients.Lock()
@ -113,7 +114,7 @@ func (clients *ClientManager) SetNick(client *Client, newNick string) error {
if currentNewEntry != nil && currentNewEntry != client {
return errNicknameInUse
}
if reservation == NickReservationStrict && reservedAccount != client.Account() {
if method == NickReservationStrict && reservedAccount != client.Account() {
return errNicknameReserved
}
clients.byNick[newcfnick] = client

View File

@ -58,40 +58,10 @@ func (conf *PassConfig) PasswordBytes() []byte {
return bytes
}
type NickReservation int
const (
NickReservationDisabled NickReservation = iota
NickReservationWithTimeout
NickReservationStrict
)
func (nr *NickReservation) UnmarshalYAML(unmarshal func(interface{}) error) error {
var orig, raw string
var err error
if err = unmarshal(&orig); err != nil {
return err
}
if raw, err = Casefold(orig); err != nil {
return err
}
if raw == "disabled" || raw == "false" || raw == "" {
*nr = NickReservationDisabled
} else if raw == "timeout" {
*nr = NickReservationWithTimeout
} else if raw == "strict" {
*nr = NickReservationStrict
} else {
return errors.New(fmt.Sprintf("invalid nick-reservation value: %s", orig))
}
return nil
}
type AccountConfig struct {
Registration AccountRegistrationConfig
AuthenticationEnabled bool `yaml:"authentication-enabled"`
NickReservation NickReservation `yaml:"nick-reservation"`
NickReservationTimeout time.Duration `yaml:"nick-reservation-timeout"`
Registration AccountRegistrationConfig
AuthenticationEnabled bool `yaml:"authentication-enabled"`
NickReservation NickReservationConfig `yaml:"nick-reservation"`
}
// AccountRegistrationConfig controls account registration.
@ -119,6 +89,39 @@ type AccountRegistrationConfig struct {
AllowMultiplePerConnection bool `yaml:"allow-multiple-per-connection"`
}
type NickReservationMethod int
const (
NickReservationWithTimeout NickReservationMethod = iota
NickReservationStrict
)
func (nr *NickReservationMethod) UnmarshalYAML(unmarshal func(interface{}) error) error {
var orig, raw string
var err error
if err = unmarshal(&orig); err != nil {
return err
}
if raw, err = Casefold(orig); err != nil {
return err
}
if raw == "timeout" {
*nr = NickReservationWithTimeout
} else if raw == "strict" {
*nr = NickReservationStrict
} else {
return errors.New(fmt.Sprintf("invalid nick-reservation.method value: %s", orig))
}
return nil
}
type NickReservationConfig struct {
Enabled bool
Method NickReservationMethod
RenameTimeout time.Duration `yaml:"rename-timeout"`
RenamePrefix string `yaml:"rename-prefix"`
}
// ChannelRegistrationConfig controls channel registration.
type ChannelRegistrationConfig struct {
Enabled bool

View File

@ -183,13 +183,13 @@ type NickTimer struct {
// NewNickTimer sets up a new nick timer (returning nil if timeout enforcement is not enabled)
func NewNickTimer(client *Client) *NickTimer {
config := client.server.AccountConfig()
if config.NickReservation != NickReservationWithTimeout {
config := client.server.AccountConfig().NickReservation
if !(config.Enabled && config.Method == NickReservationWithTimeout) {
return nil
}
nt := NickTimer{
client: client,
timeout: config.NickReservationTimeout,
timeout: config.RenameTimeout,
}
return &nt
}
@ -239,6 +239,6 @@ func (nt *NickTimer) sendWarning() {
func (nt *NickTimer) processTimeout() {
baseMsg := "Nick is reserved and authentication timeout expired: %v"
nt.client.Quit(fmt.Sprintf(nt.client.t(baseMsg), nt.timeout))
nt.client.destroy(false)
nt.client.Notice(fmt.Sprintf(nt.client.t(baseMsg), nt.timeout))
nt.client.server.RandomlyRename(nt.client)
}

View File

@ -5,6 +5,8 @@
package irc
import (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
@ -72,3 +74,18 @@ func performNickChange(server *Server, client *Client, target *Client, newnick s
}
return false
}
func (server *Server) RandomlyRename(client *Client) {
prefix := server.AccountConfig().NickReservation.RenamePrefix
if prefix == "" {
prefix = "Guest-"
}
buf := make([]byte, 8)
rand.Read(buf)
nick := fmt.Sprintf("%s%s", prefix, hex.EncodeToString(buf))
rb := NewResponseBuffer(client)
performNickChange(server, client, client, nick, rb)
rb.Send()
// technically performNickChange can fail to change the nick,
// but if they're still delinquent, the timer will get them later
}

View File

@ -810,8 +810,8 @@ func (server *Server) applyConfig(config *Config, initial bool) error {
server.accountConfig = &config.Accounts
server.configurableStateMutex.Unlock()
nickReservationPreviouslyDisabled := oldAccountConfig != nil && oldAccountConfig.NickReservation == NickReservationDisabled
nickReservationNowEnabled := config.Accounts.NickReservation != NickReservationDisabled
nickReservationPreviouslyDisabled := oldAccountConfig != nil && !oldAccountConfig.NickReservation.Enabled
nickReservationNowEnabled := config.Accounts.NickReservation.Enabled
if nickReservationPreviouslyDisabled && nickReservationNowEnabled {
server.accounts.buildNickToAccountIndex()
}
@ -1113,13 +1113,6 @@ func (server *Server) setupListeners(config *Config) {
}
}
// GetDefaultChannelModes returns our default channel modes.
func (server *Server) GetDefaultChannelModes() modes.Modes {
server.configurableStateMutex.RLock()
defer server.configurableStateMutex.RUnlock()
return server.defaultChannelModes
}
// elistMatcher takes and matches ELIST conditions
type elistMatcher struct {
MinClientsActive bool

View File

@ -159,13 +159,23 @@ accounts:
# is account authentication enabled?
authentication-enabled: true
# will the server enforce that only the account holder can use the account name as a nick?
# options:
# `disabled`: no enforcement
# `timeout` (auth to nickserv within some period of time or you're disconnected)
# `strict`: must authenticate up front with SASL
nick-reservation: disabled
nick-reservation-timeout: 30s
# nick-reservation controls how, and whether, nicknames are linked to accounts
nick-reservation:
# is there any enforcement of reserved nicknames?
enabled: false
# method describes how nickname reservation is handled
# timeout: let the user change to the registered nickname, give them X seconds
# to login and then rename them if they haven't done so
# strict: don't let the user change to the registered nickname unless they're
# already logged-in using SASL or NickServ
method: timeout
# rename-timeout - this is how long users have 'til they're renamed
rename-timeout: 30s
# rename-prefix - this is the prefix to use when renaming clients (e.g. Guest-AB54U31)
rename-prefix: Guest-
# channel options
channels: