1
0
Fork 0
forked from External/ergo

per-user settings for nickname enforcement

This commit is contained in:
Shivaram Lingamneni 2019-01-02 10:08:44 -05:00
parent d0ded906d4
commit 2ee89b15b3
7 changed files with 216 additions and 39 deletions

View file

@ -30,6 +30,7 @@ const (
keyAccountRegTime = "account.registered.time %s"
keyAccountCredentials = "account.credentials %s"
keyAccountAdditionalNicks = "account.additionalnicks %s"
keyAccountEnforcement = "account.customenforcement %s"
keyAccountVHost = "account.vhost %s"
keyCertToAccount = "account.creds.certfp %s"
@ -53,12 +54,14 @@ type AccountManager struct {
// track clients logged in to accounts
accountToClients map[string][]*Client
nickToAccount map[string]string
accountToMethod map[string]NickReservationMethod
}
func NewAccountManager(server *Server) *AccountManager {
am := AccountManager{
accountToClients: make(map[string][]*Client),
nickToAccount: make(map[string]string),
accountToMethod: make(map[string]NickReservationMethod),
server: server,
}
@ -72,7 +75,8 @@ func (am *AccountManager) buildNickToAccountIndex() {
return
}
result := make(map[string]string)
nickToAccount := make(map[string]string)
accountToMethod := make(map[string]NickReservationMethod)
existsPrefix := fmt.Sprintf(keyAccountExists, "")
am.serialCacheUpdateMutex.Lock()
@ -83,14 +87,22 @@ func (am *AccountManager) buildNickToAccountIndex() {
if !strings.HasPrefix(key, existsPrefix) {
return false
}
accountName := strings.TrimPrefix(key, existsPrefix)
if _, err := tx.Get(fmt.Sprintf(keyAccountVerified, accountName)); err == nil {
result[accountName] = accountName
account := strings.TrimPrefix(key, existsPrefix)
if _, err := tx.Get(fmt.Sprintf(keyAccountVerified, account)); err == nil {
nickToAccount[account] = account
}
if rawNicks, err := tx.Get(fmt.Sprintf(keyAccountAdditionalNicks, accountName)); err == nil {
if rawNicks, err := tx.Get(fmt.Sprintf(keyAccountAdditionalNicks, account)); err == nil {
additionalNicks := unmarshalReservedNicks(rawNicks)
for _, nick := range additionalNicks {
result[nick] = accountName
nickToAccount[nick] = account
}
}
if methodStr, err := tx.Get(fmt.Sprintf(keyAccountEnforcement, account)); err == nil {
method, err := nickReservationFromString(methodStr)
if err == nil {
accountToMethod[account] = method
}
}
return true
@ -102,7 +114,8 @@ func (am *AccountManager) buildNickToAccountIndex() {
am.server.logger.Error("internal", "couldn't read reserved nicks", err.Error())
} else {
am.Lock()
am.nickToAccount = result
am.nickToAccount = nickToAccount
am.accountToMethod = accountToMethod
am.Unlock()
}
}
@ -156,6 +169,84 @@ func (am *AccountManager) NickToAccount(nick string) string {
return am.nickToAccount[cfnick]
}
// Given a nick, looks up the account that owns it and the method (none/timeout/strict)
// used to enforce ownership.
func (am *AccountManager) EnforcementStatus(nick string) (account string, method NickReservationMethod) {
cfnick, err := CasefoldName(nick)
if err != nil {
return
}
config := am.server.Config()
if !config.Accounts.NickReservation.Enabled {
method = NickReservationNone
return
}
am.RLock()
defer am.RUnlock()
account = am.nickToAccount[cfnick]
method = am.accountToMethod[account]
// if they don't have a custom setting, or customization is disabled, use the default
if method == NickReservationOptional || !config.Accounts.NickReservation.AllowCustomEnforcement {
method = config.Accounts.NickReservation.Method
}
if method == NickReservationOptional {
// enforcement was explicitly enabled neither in the config or by the user
method = NickReservationNone
}
return
}
// Looks up the enforcement method stored in the database for an account
// (typically you want EnforcementStatus instead, which respects the config)
func (am *AccountManager) getStoredEnforcementStatus(account string) string {
am.RLock()
defer am.RUnlock()
return nickReservationToString(am.accountToMethod[account])
}
// Sets a custom enforcement method for an account and stores it in the database.
func (am *AccountManager) SetEnforcementStatus(account string, method NickReservationMethod) (err error) {
config := am.server.Config()
if !(config.Accounts.NickReservation.Enabled && config.Accounts.NickReservation.AllowCustomEnforcement) {
return errFeatureDisabled
}
var serialized string
if method == NickReservationOptional {
serialized = "" // normally this is "default", but we're going to delete the key
} else {
serialized = nickReservationToString(method)
}
key := fmt.Sprintf(keyAccountEnforcement, account)
am.Lock()
defer am.Unlock()
currentMethod := am.accountToMethod[account]
if method != currentMethod {
if method == NickReservationOptional {
delete(am.accountToMethod, account)
} else {
am.accountToMethod[account] = method
}
return am.server.store.Update(func(tx *buntdb.Tx) (err error) {
if serialized != "" {
_, _, err = tx.Set(key, nickReservationToString(method), nil)
} else {
_, err = tx.Delete(key)
}
return
})
}
return nil
}
func (am *AccountManager) AccountToClients(account string) (result []*Client) {
cfaccount, err := CasefoldName(account)
if err != nil {
@ -992,10 +1083,12 @@ func (am *AccountManager) applyVhostToClients(account string, result VHostInfo)
func (am *AccountManager) Login(client *Client, account ClientAccount) {
changed := client.SetAccountName(account.Name)
if changed {
go client.nickTimer.Touch()
if !changed {
return
}
client.nickTimer.Touch()
am.applyVHostInfo(client, account.VHost)
casefoldedAccount := client.Account()