mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-23 03:02:48 +01:00
commit
0d311cd939
@ -337,24 +337,28 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
|
||||
return errAccountAlreadyRegistered
|
||||
}
|
||||
|
||||
config := am.server.AccountConfig()
|
||||
config := am.server.Config()
|
||||
|
||||
// final "is registration allowed" check, probably redundant:
|
||||
if !(config.Registration.Enabled || callbackNamespace == "admin") {
|
||||
if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") {
|
||||
return errFeatureDisabled
|
||||
}
|
||||
|
||||
// if nick reservation is enabled, you can only register your current nickname
|
||||
// as an account; this prevents "land-grab" situations where someone else
|
||||
// registers your nick out from under you and then NS GHOSTs you
|
||||
// n.b. client is nil during a SAREGISTER:
|
||||
if config.NickReservation.Enabled && client != nil && client.NickCasefolded() != casefoldedAccount {
|
||||
// n.b. client is nil during a SAREGISTER
|
||||
// n.b. if ForceGuestFormat, then there's no concern, because you can't
|
||||
// register a guest nickname anyway, and the actual registration system
|
||||
// will prevent any double-register
|
||||
if client != nil && config.Accounts.NickReservation.Enabled &&
|
||||
!config.Accounts.NickReservation.ForceGuestFormat &&
|
||||
client.NickCasefolded() != casefoldedAccount {
|
||||
return errAccountMustHoldNick
|
||||
}
|
||||
|
||||
// can't register a guest nickname
|
||||
renamePrefix := strings.ToLower(config.NickReservation.RenamePrefix)
|
||||
if renamePrefix != "" && strings.HasPrefix(casefoldedAccount, renamePrefix) {
|
||||
if config.Accounts.NickReservation.guestRegexpFolded.MatchString(casefoldedAccount) {
|
||||
return errAccountAlreadyRegistered
|
||||
}
|
||||
|
||||
@ -382,7 +386,7 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
|
||||
callbackSpec := fmt.Sprintf("%s:%s", callbackNamespace, callbackValue)
|
||||
|
||||
var setOptions *buntdb.SetOptions
|
||||
ttl := time.Duration(config.Registration.VerifyTimeout)
|
||||
ttl := time.Duration(config.Accounts.Registration.VerifyTimeout)
|
||||
if ttl != 0 {
|
||||
setOptions = &buntdb.SetOptions{Expires: true, TTL: ttl}
|
||||
}
|
||||
@ -652,7 +656,7 @@ func (am *AccountManager) dispatchCallback(client *Client, casefoldedAccount str
|
||||
}
|
||||
|
||||
func (am *AccountManager) dispatchMailtoCallback(client *Client, casefoldedAccount string, callbackValue string) (code string, err error) {
|
||||
config := am.server.AccountConfig().Registration.Callbacks.Mailto
|
||||
config := am.server.Config().Accounts.Registration.Callbacks.Mailto
|
||||
code = utils.GenerateSecretToken()
|
||||
|
||||
subject := config.VerifyMessageSubject
|
||||
@ -811,7 +815,7 @@ func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreser
|
||||
cfnick, err := CasefoldName(nick)
|
||||
skeleton, skerr := Skeleton(nick)
|
||||
// garbage nick, or garbage options, or disabled
|
||||
nrconfig := am.server.AccountConfig().NickReservation
|
||||
nrconfig := am.server.Config().Accounts.NickReservation
|
||||
if err != nil || skerr != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled {
|
||||
return errAccountNickReservationFailed
|
||||
}
|
||||
@ -939,6 +943,10 @@ func (am *AccountManager) checkPassphrase(accountName, passphrase string) (accou
|
||||
}
|
||||
|
||||
func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName string, passphrase string) (err error) {
|
||||
// XXX check this now, so we don't allow a redundant login for an always-on client
|
||||
// even for a brief period. the other potential source of nick-account conflicts
|
||||
// is from force-nick-equals-account, but those will be caught later by
|
||||
// fixupNickEqualsAccount and if there is a conflict, they will be logged out.
|
||||
if client.registered {
|
||||
if clientAlready := am.server.clients.Get(accountName); clientAlready != nil && clientAlready.AlwaysOn() {
|
||||
return errNickAccountMismatch
|
||||
@ -1536,7 +1544,7 @@ func (am *AccountManager) VHostListRequests(limit int) (requests []PendingVHostR
|
||||
func (am *AccountManager) applyVHostInfo(client *Client, info VHostInfo) {
|
||||
// if hostserv is disabled in config, then don't grant vhosts
|
||||
// that were previously approved while it was enabled
|
||||
if !am.server.AccountConfig().VHosts.Enabled {
|
||||
if !am.server.Config().Accounts.VHosts.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -116,23 +116,9 @@ func (clients *ClientManager) Resume(oldClient *Client, session *Session) (err e
|
||||
// SetNick sets a client's nickname, validating it against nicknames in use
|
||||
func (clients *ClientManager) SetNick(client *Client, session *Session, newNick string) (setNick string, err error) {
|
||||
config := client.server.Config()
|
||||
newcfnick, err := CasefoldName(newNick)
|
||||
if err != nil {
|
||||
return "", errNicknameInvalid
|
||||
}
|
||||
if len(newNick) > config.Limits.NickLen || len(newcfnick) > config.Limits.NickLen {
|
||||
return "", errNicknameInvalid
|
||||
}
|
||||
newSkeleton, err := Skeleton(newNick)
|
||||
if err != nil {
|
||||
return "", errNicknameInvalid
|
||||
}
|
||||
|
||||
if restrictedCasefoldedNicks[newcfnick] || restrictedSkeletons[newSkeleton] {
|
||||
return "", errNicknameInvalid
|
||||
}
|
||||
var newcfnick, newSkeleton string
|
||||
|
||||
reservedAccount, method := client.server.accounts.EnforcementStatus(newcfnick, newSkeleton)
|
||||
client.stateMutex.RLock()
|
||||
account := client.account
|
||||
accountName := client.accountName
|
||||
@ -141,19 +127,58 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
|
||||
realname := client.realname
|
||||
client.stateMutex.RUnlock()
|
||||
|
||||
// recompute this (client.alwaysOn is not set for unregistered clients):
|
||||
alwaysOn := account != "" && persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
|
||||
// recompute always-on status, because client.alwaysOn is not set for unregistered clients
|
||||
var alwaysOn, useAccountName bool
|
||||
if account != "" {
|
||||
alwaysOn = persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
|
||||
useAccountName = alwaysOn || config.Accounts.NickReservation.ForceNickEqualsAccount
|
||||
}
|
||||
|
||||
if alwaysOn && registered {
|
||||
return "", errCantChangeNick
|
||||
if useAccountName {
|
||||
if registered && newNick != accountName && newNick != "" {
|
||||
return "", errNickAccountMismatch
|
||||
}
|
||||
newNick = accountName
|
||||
newcfnick = account
|
||||
newSkeleton, err = Skeleton(newNick)
|
||||
if err != nil {
|
||||
return "", errNicknameInvalid
|
||||
}
|
||||
} else {
|
||||
newNick = strings.TrimSpace(newNick)
|
||||
if len(newNick) == 0 {
|
||||
return "", errNickMissing
|
||||
}
|
||||
|
||||
if account == "" && config.Accounts.NickReservation.ForceGuestFormat {
|
||||
newNick = strings.Replace(config.Accounts.NickReservation.GuestFormat, "*", newNick, 1)
|
||||
}
|
||||
|
||||
newcfnick, err = CasefoldName(newNick)
|
||||
if err != nil {
|
||||
return "", errNicknameInvalid
|
||||
}
|
||||
if len(newNick) > config.Limits.NickLen || len(newcfnick) > config.Limits.NickLen {
|
||||
return "", errNicknameInvalid
|
||||
}
|
||||
newSkeleton, err = Skeleton(newNick)
|
||||
if err != nil {
|
||||
return "", errNicknameInvalid
|
||||
}
|
||||
|
||||
if restrictedCasefoldedNicks[newcfnick] || restrictedSkeletons[newSkeleton] {
|
||||
return "", errNicknameInvalid
|
||||
}
|
||||
|
||||
reservedAccount, method := client.server.accounts.EnforcementStatus(newcfnick, newSkeleton)
|
||||
if method == NickEnforcementStrict && reservedAccount != "" && reservedAccount != account {
|
||||
return "", errNicknameReserved
|
||||
}
|
||||
}
|
||||
|
||||
var bouncerAllowed bool
|
||||
if config.Accounts.Multiclient.Enabled {
|
||||
if alwaysOn {
|
||||
// ignore the pre-reg nick, force a reattach
|
||||
newNick = accountName
|
||||
newcfnick = account
|
||||
if useAccountName {
|
||||
bouncerAllowed = true
|
||||
} else {
|
||||
if config.Accounts.Multiclient.AllowedByDefault && settings.AllowBouncer != MulticlientDisallowedByUser {
|
||||
@ -198,9 +223,7 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
|
||||
if skeletonHolder != nil && skeletonHolder != client {
|
||||
return "", errNicknameInUse
|
||||
}
|
||||
if method == NickEnforcementStrict && reservedAccount != "" && reservedAccount != account {
|
||||
return "", errNicknameReserved
|
||||
}
|
||||
|
||||
clients.removeInternal(client)
|
||||
clients.byNick[newcfnick] = client
|
||||
clients.bySkeleton[newSkeleton] = client
|
||||
|
@ -7,6 +7,7 @@ package irc
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@ -242,11 +243,24 @@ type AccountConfig struct {
|
||||
Duration time.Duration
|
||||
MaxAttempts int `yaml:"max-attempts"`
|
||||
} `yaml:"login-throttling"`
|
||||
SkipServerPassword bool `yaml:"skip-server-password"`
|
||||
NickReservation NickReservationConfig `yaml:"nick-reservation"`
|
||||
Multiclient MulticlientConfig
|
||||
Bouncer *MulticlientConfig // # handle old name for 'multiclient'
|
||||
VHosts VHostConfig
|
||||
SkipServerPassword bool `yaml:"skip-server-password"`
|
||||
NickReservation struct {
|
||||
Enabled bool
|
||||
AdditionalNickLimit int `yaml:"additional-nick-limit"`
|
||||
Method NickEnforcementMethod
|
||||
AllowCustomEnforcement bool `yaml:"allow-custom-enforcement"`
|
||||
RenameTimeout time.Duration `yaml:"rename-timeout"`
|
||||
// RenamePrefix is the legacy field, GuestFormat is the new version
|
||||
RenamePrefix string `yaml:"rename-prefix"`
|
||||
GuestFormat string `yaml:"guest-nickname-format"`
|
||||
guestRegexp *regexp.Regexp
|
||||
guestRegexpFolded *regexp.Regexp
|
||||
ForceGuestFormat bool `yaml:"force-guest-format"`
|
||||
ForceNickEqualsAccount bool `yaml:"force-nick-equals-account"`
|
||||
} `yaml:"nick-reservation"`
|
||||
Multiclient MulticlientConfig
|
||||
Bouncer *MulticlientConfig // # handle old name for 'multiclient'
|
||||
VHosts VHostConfig
|
||||
}
|
||||
|
||||
// AccountRegistrationConfig controls account registration.
|
||||
@ -371,15 +385,6 @@ func (cm *Casemapping) UnmarshalYAML(unmarshal func(interface{}) error) (err err
|
||||
return nil
|
||||
}
|
||||
|
||||
type NickReservationConfig struct {
|
||||
Enabled bool
|
||||
AdditionalNickLimit int `yaml:"additional-nick-limit"`
|
||||
Method NickEnforcementMethod
|
||||
AllowCustomEnforcement bool `yaml:"allow-custom-enforcement"`
|
||||
RenameTimeout time.Duration `yaml:"rename-timeout"`
|
||||
RenamePrefix string `yaml:"rename-prefix"`
|
||||
}
|
||||
|
||||
// ChannelRegistrationConfig controls channel registration.
|
||||
type ChannelRegistrationConfig struct {
|
||||
Enabled bool
|
||||
@ -883,6 +888,19 @@ func LoadConfig(filename string) (config *Config, err error) {
|
||||
config.Accounts.Multiclient.AllowedByDefault = true
|
||||
}
|
||||
|
||||
// handle guest format, including the legacy key rename-prefix
|
||||
if config.Accounts.NickReservation.GuestFormat == "" {
|
||||
renamePrefix := config.Accounts.NickReservation.RenamePrefix
|
||||
if renamePrefix == "" {
|
||||
renamePrefix = "Guest-"
|
||||
}
|
||||
config.Accounts.NickReservation.GuestFormat = renamePrefix + "*"
|
||||
}
|
||||
config.Accounts.NickReservation.guestRegexp, config.Accounts.NickReservation.guestRegexpFolded, err = compileGuestRegexp(config.Accounts.NickReservation.GuestFormat, config.Server.Casemapping)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var newLogConfigs []logger.LoggingConfig
|
||||
for _, logConfig := range config.Logging {
|
||||
// methods
|
||||
@ -1163,3 +1181,29 @@ func (config *Config) Diff(oldConfig *Config) (addedCaps, removedCaps *caps.Set)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func compileGuestRegexp(guestFormat string, casemapping Casemapping) (standard, folded *regexp.Regexp, err error) {
|
||||
starIndex := strings.IndexByte(guestFormat, '*')
|
||||
if starIndex == -1 {
|
||||
return nil, nil, errors.New("guest format must contain exactly one *")
|
||||
}
|
||||
initial := guestFormat[:starIndex]
|
||||
final := guestFormat[starIndex+1:]
|
||||
if strings.IndexByte(final, '*') != -1 {
|
||||
return nil, nil, errors.New("guest format must contain exactly one *")
|
||||
}
|
||||
standard, err = regexp.Compile(fmt.Sprintf("^%s(.*)%s$", initial, final))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
initialFolded, err := casefoldWithSetting(initial, casemapping)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
finalFolded, err := casefoldWithSetting(final, casemapping)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
folded, err = regexp.Compile(fmt.Sprintf("^%s(.*)%s$", initialFolded, finalFolded))
|
||||
return
|
||||
}
|
||||
|
@ -41,8 +41,7 @@ var (
|
||||
errNicknameInvalid = errors.New("invalid nickname")
|
||||
errNicknameInUse = errors.New("nickname in use")
|
||||
errNicknameReserved = errors.New("nickname is reserved")
|
||||
errCantChangeNick = errors.New(`Always-on clients can't change nicknames`)
|
||||
errNickAccountMismatch = errors.New(`Your nickname doesn't match your account name`)
|
||||
errNickAccountMismatch = errors.New(`Your nickname must match your account name; try logging out and logging back in with SASL`)
|
||||
errNoExistingBan = errors.New("Ban does not exist")
|
||||
errNoSuchChannel = errors.New(`No such channel`)
|
||||
errChannelPurged = errors.New(`This channel was purged by the server operators and cannot be used`)
|
||||
|
@ -25,10 +25,6 @@ func (server *Server) ChannelRegistrationEnabled() bool {
|
||||
return server.Config().Channels.Registration.Enabled
|
||||
}
|
||||
|
||||
func (server *Server) AccountConfig() *AccountConfig {
|
||||
return &server.Config().Accounts
|
||||
}
|
||||
|
||||
func (server *Server) GetOperator(name string) (oper *Oper) {
|
||||
name, err := CasefoldName(name)
|
||||
if err != nil {
|
||||
|
@ -32,7 +32,7 @@ import (
|
||||
)
|
||||
|
||||
// helper function to parse ACC callbacks, e.g., mailto:person@example.com, tel:16505551234
|
||||
func parseCallback(spec string, config *AccountConfig) (callbackNamespace string, callbackValue string) {
|
||||
func parseCallback(spec string, config AccountConfig) (callbackNamespace string, callbackValue string) {
|
||||
callback := strings.ToLower(spec)
|
||||
if callback == "*" {
|
||||
callbackNamespace = "*"
|
||||
@ -127,7 +127,7 @@ func authenticateHandler(server *Server, client *Client, msg ircmsg.IrcMessage,
|
||||
}
|
||||
|
||||
// sasl abort
|
||||
if !server.AccountConfig().AuthenticationEnabled || len(msg.Params) == 1 && msg.Params[0] == "*" {
|
||||
if !config.Accounts.AuthenticationEnabled || len(msg.Params) == 1 && msg.Params[0] == "*" {
|
||||
rb.Add(nil, server.name, ERR_SASLABORTED, details.nick, client.t("SASL authentication aborted"))
|
||||
session.sasl.Clear()
|
||||
return false
|
||||
@ -237,6 +237,8 @@ func authPlainHandler(server *Server, client *Client, mechanism string, value []
|
||||
msg := authErrorToMessage(server, err)
|
||||
rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
|
||||
return false
|
||||
} else if !fixupNickEqualsAccount(client, rb, server.Config()) {
|
||||
return false
|
||||
}
|
||||
|
||||
sendSuccessfulAccountAuth(client, rb, false, true)
|
||||
@ -245,7 +247,7 @@ func authPlainHandler(server *Server, client *Client, mechanism string, value []
|
||||
|
||||
func authErrorToMessage(server *Server, err error) (msg string) {
|
||||
switch err {
|
||||
case errAccountDoesNotExist, errAccountUnverified, errAccountInvalidCredentials, errAuthzidAuthcidMismatch:
|
||||
case errAccountDoesNotExist, errAccountUnverified, errAccountInvalidCredentials, errAuthzidAuthcidMismatch, errNickAccountMismatch:
|
||||
return err.Error()
|
||||
default:
|
||||
// don't expose arbitrary error messages to the user
|
||||
@ -280,6 +282,8 @@ func authExternalHandler(server *Server, client *Client, mechanism string, value
|
||||
msg := authErrorToMessage(server, err)
|
||||
rb.Add(nil, server.name, ERR_SASLFAIL, client.nick, fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
|
||||
return false
|
||||
} else if !fixupNickEqualsAccount(client, rb, server.Config()) {
|
||||
return false
|
||||
}
|
||||
|
||||
sendSuccessfulAccountAuth(client, rb, false, true)
|
||||
|
@ -178,7 +178,7 @@ func hsNotice(rb *ResponseBuffer, text string) {
|
||||
|
||||
// hsNotifyChannel notifies the designated channel of new vhost activity
|
||||
func hsNotifyChannel(server *Server, message string) {
|
||||
chname := server.AccountConfig().VHosts.UserRequests.Channel
|
||||
chname := server.Config().Accounts.VHosts.UserRequests.Channel
|
||||
channel := server.channels.Get(chname)
|
||||
if channel == nil {
|
||||
return
|
||||
@ -280,11 +280,11 @@ func hsStatusHandler(server *Server, client *Client, command string, params []st
|
||||
}
|
||||
|
||||
func validateVhost(server *Server, vhost string, oper bool) error {
|
||||
ac := server.AccountConfig()
|
||||
if len(vhost) > ac.VHosts.MaxLength {
|
||||
config := server.Config()
|
||||
if len(vhost) > config.Accounts.VHosts.MaxLength {
|
||||
return errVHostTooLong
|
||||
}
|
||||
if !ac.VHosts.ValidRegexp.MatchString(vhost) {
|
||||
if !config.Accounts.VHosts.ValidRegexp.MatchString(vhost) {
|
||||
return errVHostBadCharacters
|
||||
}
|
||||
return nil
|
||||
|
@ -6,7 +6,6 @@ package irc
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@ -27,31 +26,28 @@ var (
|
||||
)
|
||||
|
||||
// returns whether the change succeeded or failed
|
||||
func performNickChange(server *Server, client *Client, target *Client, session *Session, newnick string, rb *ResponseBuffer) bool {
|
||||
nickname := strings.TrimSpace(newnick)
|
||||
func performNickChange(server *Server, client *Client, target *Client, session *Session, nickname string, rb *ResponseBuffer) bool {
|
||||
currentNick := client.Nick()
|
||||
|
||||
if len(nickname) < 1 {
|
||||
rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, currentNick, client.t("No nickname given"))
|
||||
return false
|
||||
}
|
||||
|
||||
if target.Nick() == nickname {
|
||||
details := target.Details()
|
||||
if details.nick == nickname {
|
||||
return true
|
||||
}
|
||||
hadNick := details.nick != "*"
|
||||
origNickMask := details.nickMask
|
||||
|
||||
hadNick := target.HasNick()
|
||||
origNickMask := target.NickMaskString()
|
||||
details := target.Details()
|
||||
assignedNickname, err := client.server.clients.SetNick(target, session, nickname)
|
||||
if err == errNicknameInUse {
|
||||
rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, nickname, client.t("Nickname is already in use"))
|
||||
rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, utils.SafeErrorParam(nickname), client.t("Nickname is already in use"))
|
||||
} else if err == errNicknameReserved {
|
||||
rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, nickname, client.t("Nickname is reserved by a different account"))
|
||||
rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, utils.SafeErrorParam(nickname), client.t("Nickname is reserved by a different account"))
|
||||
} else if err == errNicknameInvalid {
|
||||
rb.Add(nil, server.name, ERR_ERRONEUSNICKNAME, currentNick, utils.SafeErrorParam(nickname), client.t("Erroneous nickname"))
|
||||
} else if err == errCantChangeNick {
|
||||
rb.Add(nil, server.name, ERR_NICKNAMEINUSE, currentNick, utils.SafeErrorParam(nickname), client.t(err.Error()))
|
||||
} else if err == errNickAccountMismatch {
|
||||
// this used to use ERR_NICKNAMEINUSE, but it displayed poorly in some clients;
|
||||
// ERR_UNKNOWNERROR at least has a better chance of displaying our error text
|
||||
rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", client.t(err.Error()))
|
||||
} else if err == errNickMissing {
|
||||
rb.Add(nil, server.name, ERR_NONICKNAMEGIVEN, currentNick, client.t("No nickname given"))
|
||||
} else if err != nil {
|
||||
rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error()))
|
||||
}
|
||||
@ -96,13 +92,10 @@ func performNickChange(server *Server, client *Client, target *Client, session *
|
||||
}
|
||||
|
||||
func (server *Server) RandomlyRename(client *Client) {
|
||||
prefix := server.AccountConfig().NickReservation.RenamePrefix
|
||||
if prefix == "" {
|
||||
prefix = "Guest-"
|
||||
}
|
||||
format := server.Config().Accounts.NickReservation.GuestFormat
|
||||
buf := make([]byte, 8)
|
||||
rand.Read(buf)
|
||||
nick := fmt.Sprintf("%s%s", prefix, hex.EncodeToString(buf))
|
||||
nick := strings.Replace(format, "*", utils.B32Encoder.EncodeToString(buf), -1)
|
||||
sessions := client.Sessions()
|
||||
if len(sessions) == 0 {
|
||||
return
|
||||
@ -115,3 +108,23 @@ func (server *Server) RandomlyRename(client *Client) {
|
||||
// technically performNickChange can fail to change the nick,
|
||||
// but if they're still delinquent, the timer will get them later
|
||||
}
|
||||
|
||||
// if force-nick-equals-account is set, account name and nickname must be equal,
|
||||
// so we need to re-NICK automatically on every login event (IDENTIFY,
|
||||
// VERIFY, and a REGISTER that auto-verifies). if we can't get the nick
|
||||
// then we log them out (they will be able to reattach with SASL)
|
||||
func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config) (success bool) {
|
||||
if !config.Accounts.NickReservation.ForceNickEqualsAccount {
|
||||
return true
|
||||
}
|
||||
if !client.registered {
|
||||
return true
|
||||
}
|
||||
// don't need to supply a nickname, SetNick will use the account name
|
||||
if !performNickChange(client.server, client, client, rb.session, "", rb) {
|
||||
client.server.accounts.Logout(client)
|
||||
nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL"))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -649,12 +649,19 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
|
||||
loginSuccessful = (err == nil)
|
||||
}
|
||||
|
||||
nickFixupFailed := false
|
||||
if loginSuccessful {
|
||||
if !fixupNickEqualsAccount(client, rb, server.Config()) {
|
||||
loginSuccessful = false
|
||||
// fixupNickEqualsAccount sends its own error message, don't send another
|
||||
nickFixupFailed = true
|
||||
}
|
||||
}
|
||||
|
||||
if loginSuccessful {
|
||||
sendSuccessfulAccountAuth(client, rb, true, true)
|
||||
} else if err == errNickAccountMismatch {
|
||||
nsNotice(rb, client.t("That account is set to always-on; try logging out and logging back in with SASL"))
|
||||
} else {
|
||||
nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password"))
|
||||
} else if !nickFixupFailed {
|
||||
nsNotice(rb, fmt.Sprintf(client.t("Authentication failed: %s"), authErrorToMessage(server, err)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -667,7 +674,7 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
|
||||
var accountName string
|
||||
if len(params) > 0 {
|
||||
nick := params[0]
|
||||
if server.AccountConfig().NickReservation.Enabled {
|
||||
if server.Config().Accounts.NickReservation.Enabled {
|
||||
accountName = server.accounts.NickToAccount(nick)
|
||||
if accountName == "" {
|
||||
nsNotice(rb, client.t("That nickname is not registered"))
|
||||
@ -704,7 +711,6 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
|
||||
|
||||
func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
|
||||
details := client.Details()
|
||||
account := details.nick
|
||||
passphrase := params[0]
|
||||
var email string
|
||||
if 1 < len(params) {
|
||||
@ -730,10 +736,20 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
|
||||
return
|
||||
}
|
||||
|
||||
config := server.AccountConfig()
|
||||
config := server.Config()
|
||||
account := details.nick
|
||||
if config.Accounts.NickReservation.ForceGuestFormat {
|
||||
matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account)
|
||||
if matches == nil || len(matches) < 2 {
|
||||
nsNotice(rb, client.t("Erroneous nickname"))
|
||||
return
|
||||
}
|
||||
account = matches[1]
|
||||
}
|
||||
|
||||
var callbackNamespace, callbackValue string
|
||||
noneCallbackAllowed := false
|
||||
for _, callback := range config.Registration.EnabledCallbacks {
|
||||
for _, callback := range config.Accounts.Registration.EnabledCallbacks {
|
||||
if callback == "*" {
|
||||
noneCallbackAllowed = true
|
||||
}
|
||||
@ -744,7 +760,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
|
||||
if noneCallbackAllowed {
|
||||
callbackNamespace = "*"
|
||||
} else {
|
||||
callbackNamespace, callbackValue = parseCallback(email, config)
|
||||
callbackNamespace, callbackValue = parseCallback(email, config.Accounts)
|
||||
if callbackNamespace == "" || callbackValue == "" {
|
||||
nsNotice(rb, client.t("Registration requires a valid e-mail address"))
|
||||
return
|
||||
@ -755,7 +771,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
|
||||
if err == nil {
|
||||
if callbackNamespace == "*" {
|
||||
err = server.accounts.Verify(client, account, "")
|
||||
if err == nil {
|
||||
if err == nil && fixupNickEqualsAccount(client, rb, config) {
|
||||
sendSuccessfulRegResponse(client, rb, true)
|
||||
}
|
||||
} else {
|
||||
@ -861,7 +877,9 @@ func nsVerifyHandler(server *Server, client *Client, command string, params []st
|
||||
return
|
||||
}
|
||||
|
||||
sendSuccessfulRegResponse(client, rb, true)
|
||||
if fixupNickEqualsAccount(client, rb, server.Config()) {
|
||||
sendSuccessfulRegResponse(client, rb, true)
|
||||
}
|
||||
}
|
||||
|
||||
func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
|
||||
|
@ -76,7 +76,11 @@ func iterateFolding(profile *precis.Profile, oldStr string) (str string, err err
|
||||
|
||||
// Casefold returns a casefolded string, without doing any name or channel character checks.
|
||||
func Casefold(str string) (string, error) {
|
||||
switch globalCasemappingSetting {
|
||||
return casefoldWithSetting(str, globalCasemappingSetting)
|
||||
}
|
||||
|
||||
func casefoldWithSetting(str string, setting Casemapping) (string, error) {
|
||||
switch setting {
|
||||
default:
|
||||
return iterateFolding(precis.UsernameCaseMapped, str)
|
||||
case CasemappingASCII:
|
||||
|
22
oragono.yaml
22
oragono.yaml
@ -343,8 +343,26 @@ accounts:
|
||||
# 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-
|
||||
# format for guest nicknames:
|
||||
# 1. these nicknames cannot be registered or reserved
|
||||
# 2. if a client is automatically renamed by the server,
|
||||
# this is the template that will be used (e.g., Guest-nccj6rgmt97cg)
|
||||
# 3. if enforce-guest-format (see below) is enabled, clients without
|
||||
# a registered account will have this template applied to their
|
||||
# nicknames (e.g., 'katie' will become 'Guest-katie')
|
||||
guest-nickname-format: "Guest-*"
|
||||
|
||||
# when enabled, forces users not logged into an account to use
|
||||
# a nickname matching the guest template. a caveat: this may prevent
|
||||
# users from choosing nicknames in scripts different from the guest
|
||||
# nickname format.
|
||||
force-guest-format: false
|
||||
|
||||
# when enabled, forces users logged into an account to use the
|
||||
# account name as their nickname. when combined with strict nickname
|
||||
# enforcement, this lets users treat nicknames and account names
|
||||
# as equivalent for the purpose of ban/invite/exception lists.
|
||||
force-nick-equals-account: false
|
||||
|
||||
# multiclient controls whether oragono allows multiple connections to
|
||||
# attach to the same client/nickname identity; this is part of the
|
||||
|
Loading…
Reference in New Issue
Block a user