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() { func (am *AccountManager) buildNickToAccountIndex() {
if am.server.AccountConfig().NickReservation == NickReservationDisabled { if am.server.AccountConfig().NickReservation.Enabled {
return return
} }
@ -98,6 +98,12 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
return errAccountCreation 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) accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount) accountNameKey := fmt.Sprintf(keyAccountName, casefoldedAccount)
registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount) registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)

View File

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

View File

@ -58,40 +58,10 @@ func (conf *PassConfig) PasswordBytes() []byte {
return bytes 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 { type AccountConfig struct {
Registration AccountRegistrationConfig Registration AccountRegistrationConfig
AuthenticationEnabled bool `yaml:"authentication-enabled"` AuthenticationEnabled bool `yaml:"authentication-enabled"`
NickReservation NickReservation `yaml:"nick-reservation"` NickReservation NickReservationConfig `yaml:"nick-reservation"`
NickReservationTimeout time.Duration `yaml:"nick-reservation-timeout"`
} }
// AccountRegistrationConfig controls account registration. // AccountRegistrationConfig controls account registration.
@ -119,6 +89,39 @@ type AccountRegistrationConfig struct {
AllowMultiplePerConnection bool `yaml:"allow-multiple-per-connection"` 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. // ChannelRegistrationConfig controls channel registration.
type ChannelRegistrationConfig struct { type ChannelRegistrationConfig struct {
Enabled bool 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) // NewNickTimer sets up a new nick timer (returning nil if timeout enforcement is not enabled)
func NewNickTimer(client *Client) *NickTimer { func NewNickTimer(client *Client) *NickTimer {
config := client.server.AccountConfig() config := client.server.AccountConfig().NickReservation
if config.NickReservation != NickReservationWithTimeout { if !(config.Enabled && config.Method == NickReservationWithTimeout) {
return nil return nil
} }
nt := NickTimer{ nt := NickTimer{
client: client, client: client,
timeout: config.NickReservationTimeout, timeout: config.RenameTimeout,
} }
return &nt return &nt
} }
@ -239,6 +239,6 @@ func (nt *NickTimer) sendWarning() {
func (nt *NickTimer) processTimeout() { func (nt *NickTimer) processTimeout() {
baseMsg := "Nick is reserved and authentication timeout expired: %v" baseMsg := "Nick is reserved and authentication timeout expired: %v"
nt.client.Quit(fmt.Sprintf(nt.client.t(baseMsg), nt.timeout)) nt.client.Notice(fmt.Sprintf(nt.client.t(baseMsg), nt.timeout))
nt.client.destroy(false) nt.client.server.RandomlyRename(nt.client)
} }

View File

@ -5,6 +5,8 @@
package irc package irc
import ( import (
"crypto/rand"
"encoding/hex"
"fmt" "fmt"
"strings" "strings"
@ -72,3 +74,18 @@ func performNickChange(server *Server, client *Client, target *Client, newnick s
} }
return false 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.accountConfig = &config.Accounts
server.configurableStateMutex.Unlock() server.configurableStateMutex.Unlock()
nickReservationPreviouslyDisabled := oldAccountConfig != nil && oldAccountConfig.NickReservation == NickReservationDisabled nickReservationPreviouslyDisabled := oldAccountConfig != nil && !oldAccountConfig.NickReservation.Enabled
nickReservationNowEnabled := config.Accounts.NickReservation != NickReservationDisabled nickReservationNowEnabled := config.Accounts.NickReservation.Enabled
if nickReservationPreviouslyDisabled && nickReservationNowEnabled { if nickReservationPreviouslyDisabled && nickReservationNowEnabled {
server.accounts.buildNickToAccountIndex() 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 // elistMatcher takes and matches ELIST conditions
type elistMatcher struct { type elistMatcher struct {
MinClientsActive bool MinClientsActive bool

View File

@ -159,13 +159,23 @@ accounts:
# is account authentication enabled? # is account authentication enabled?
authentication-enabled: true authentication-enabled: true
# will the server enforce that only the account holder can use the account name as a nick? # nick-reservation controls how, and whether, nicknames are linked to accounts
# options: nick-reservation:
# `disabled`: no enforcement # is there any enforcement of reserved nicknames?
# `timeout` (auth to nickserv within some period of time or you're disconnected) enabled: false
# `strict`: must authenticate up front with SASL
nick-reservation: disabled # method describes how nickname reservation is handled
nick-reservation-timeout: 30s # 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 # channel options
channels: channels: