3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-10 22:19:31 +01:00

Merge pull request #767 from slingamn/issue764_hstake_again

fix #764
This commit is contained in:
Shivaram Lingamneni 2020-02-02 10:09:24 -08:00 committed by GitHub
commit 806c4b0d68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 53 additions and 15 deletions

View File

@ -1169,13 +1169,9 @@ func (vh *VHostInfo) checkThrottle(cooldown time.Duration) (err error) {
// callback type implementing the actual business logic of vhost operations // callback type implementing the actual business logic of vhost operations
type vhostMunger func(input VHostInfo) (output VHostInfo, err error) type vhostMunger func(input VHostInfo) (output VHostInfo, err error)
func (am *AccountManager) VHostSet(account string, vhost string, cooldown time.Duration) (result VHostInfo, err error) { func (am *AccountManager) VHostSet(account string, vhost string) (result VHostInfo, err error) {
munger := func(input VHostInfo) (output VHostInfo, err error) { munger := func(input VHostInfo) (output VHostInfo, err error) {
output = input output = input
err = output.checkThrottle(cooldown)
if err != nil {
return
}
output.Enabled = true output.Enabled = true
output.ApprovedVHost = vhost output.ApprovedVHost = vhost
return return
@ -1187,6 +1183,10 @@ func (am *AccountManager) VHostSet(account string, vhost string, cooldown time.D
func (am *AccountManager) VHostRequest(account string, vhost string, cooldown time.Duration) (result VHostInfo, err error) { func (am *AccountManager) VHostRequest(account string, vhost string, cooldown time.Duration) (result VHostInfo, err error) {
munger := func(input VHostInfo) (output VHostInfo, err error) { munger := func(input VHostInfo) (output VHostInfo, err error) {
output = input output = input
if input.Forbidden {
err = errVhostsForbidden
return
}
// you can update your existing request, but if you were approved or rejected, // you can update your existing request, but if you were approved or rejected,
// you can't spam a new request // you can't spam a new request
if output.RequestedVHost == "" { if output.RequestedVHost == "" {
@ -1205,6 +1205,32 @@ func (am *AccountManager) VHostRequest(account string, vhost string, cooldown ti
return am.performVHostChange(account, munger) return am.performVHostChange(account, munger)
} }
func (am *AccountManager) VHostTake(account string, vhost string, cooldown time.Duration) (result VHostInfo, err error) {
munger := func(input VHostInfo) (output VHostInfo, err error) {
output = input
if input.Forbidden {
err = errVhostsForbidden
return
}
// if you have a request pending, you can cancel it using take;
// otherwise, you're subject to the same throttling as if you were making a request
if output.RequestedVHost == "" {
err = output.checkThrottle(cooldown)
}
if err != nil {
return
}
output.ApprovedVHost = vhost
output.RequestedVHost = ""
output.RejectedVHost = ""
output.RejectionReason = ""
output.LastRequestTime = time.Now().UTC()
return
}
return am.performVHostChange(account, munger)
}
func (am *AccountManager) VHostApprove(account string) (result VHostInfo, err error) { func (am *AccountManager) VHostApprove(account string) (result VHostInfo, err error) {
munger := func(input VHostInfo) (output VHostInfo, err error) { munger := func(input VHostInfo) (output VHostInfo, err error) {
output = input output = input

View File

@ -50,6 +50,7 @@ var (
errBanned = errors.New("IP or nickmask banned") errBanned = errors.New("IP or nickmask banned")
errInvalidParams = utils.ErrInvalidParams errInvalidParams = utils.ErrInvalidParams
errNoVhost = errors.New(`You do not have an approved vhost`) errNoVhost = errors.New(`You do not have an approved vhost`)
errVhostsForbidden = errors.New(`An administrator has denied you the ability to use vhosts`)
errLimitExceeded = errors.New("Limit exceeded") errLimitExceeded = errors.New("Limit exceeded")
errNoop = errors.New("Action was a no-op") errNoop = errors.New("Action was a no-op")
errCASFailed = errors.New("Compare-and-swap update of database value failed") errCASFailed = errors.New("Compare-and-swap update of database value failed")

View File

@ -7,6 +7,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"regexp" "regexp"
"github.com/oragono/oragono/irc/sno"
) )
const hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed const hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
@ -216,6 +218,8 @@ func hsRequestHandler(server *Server, client *Client, command string, params []s
if err != nil { if err != nil {
if throttled, ok := err.(*vhostThrottleExceeded); ok { if throttled, ok := err.(*vhostThrottleExceeded); ok {
hsNotice(rb, fmt.Sprintf(client.t("You must wait an additional %v before making another request"), throttled.timeRemaining)) hsNotice(rb, fmt.Sprintf(client.t("You must wait an additional %v before making another request"), throttled.timeRemaining))
} else if err == errVhostsForbidden {
hsNotice(rb, client.t("An administrator has denied you the ability to use vhosts"))
} else { } else {
hsNotice(rb, client.t("An error occurred")) hsNotice(rb, client.t("An error occurred"))
} }
@ -223,7 +227,7 @@ func hsRequestHandler(server *Server, client *Client, command string, params []s
hsNotice(rb, client.t("Your vhost request will be reviewed by an administrator")) hsNotice(rb, client.t("Your vhost request will be reviewed by an administrator"))
chanMsg := fmt.Sprintf("Account %s requests vhost %s", accountName, vhost) chanMsg := fmt.Sprintf("Account %s requests vhost %s", accountName, vhost)
hsNotifyChannel(server, chanMsg) hsNotifyChannel(server, chanMsg)
// TODO send admins a snomask of some kind server.snomasks.Send(sno.LocalVhosts, chanMsg)
} }
} }
@ -298,7 +302,7 @@ func hsSetHandler(server *Server, client *Client, command string, params []strin
} }
// else: command == "del", vhost == "" // else: command == "del", vhost == ""
_, err := server.accounts.VHostSet(user, vhost, 0) _, err := server.accounts.VHostSet(user, vhost)
if err != nil { if err != nil {
hsNotice(rb, client.t("An error occurred")) hsNotice(rb, client.t("An error occurred"))
} else if vhost != "" { } else if vhost != "" {
@ -326,6 +330,7 @@ func hsApproveHandler(server *Server, client *Client, command string, params []s
hsNotice(rb, fmt.Sprintf(client.t("Successfully approved vhost request for %s"), user)) hsNotice(rb, fmt.Sprintf(client.t("Successfully approved vhost request for %s"), user))
chanMsg := fmt.Sprintf("Oper %[1]s approved vhost %[2]s for account %[3]s", client.Nick(), vhostInfo.ApprovedVHost, user) chanMsg := fmt.Sprintf("Oper %[1]s approved vhost %[2]s for account %[3]s", client.Nick(), vhostInfo.ApprovedVHost, user)
hsNotifyChannel(server, chanMsg) hsNotifyChannel(server, chanMsg)
server.snomasks.Send(sno.LocalVhosts, chanMsg)
for _, client := range server.accounts.AccountToClients(user) { for _, client := range server.accounts.AccountToClients(user) {
client.Notice(client.t("Your vhost request was approved by an administrator")) client.Notice(client.t("Your vhost request was approved by an administrator"))
} }
@ -346,6 +351,7 @@ func hsRejectHandler(server *Server, client *Client, command string, params []st
hsNotice(rb, fmt.Sprintf(client.t("Successfully rejected vhost request for %s"), user)) hsNotice(rb, fmt.Sprintf(client.t("Successfully rejected vhost request for %s"), user))
chanMsg := fmt.Sprintf("Oper %s rejected vhost %s for account %s, with the reason: %v", client.Nick(), vhostInfo.RejectedVHost, user, reason) chanMsg := fmt.Sprintf("Oper %s rejected vhost %s for account %s, with the reason: %v", client.Nick(), vhostInfo.RejectedVHost, user, reason)
hsNotifyChannel(server, chanMsg) hsNotifyChannel(server, chanMsg)
server.snomasks.Send(sno.LocalVhosts, chanMsg)
for _, client := range server.accounts.AccountToClients(user) { for _, client := range server.accounts.AccountToClients(user) {
if reason == "" { if reason == "" {
client.Notice("Your vhost request was rejected by an administrator") client.Notice("Your vhost request was rejected by an administrator")
@ -404,16 +410,18 @@ func hsTakeHandler(server *Server, client *Client, command string, params []stri
return return
} }
_, err := server.accounts.VHostSet(client.Account(), vhost, config.Accounts.VHosts.UserRequests.Cooldown) account := client.Account()
_, err := server.accounts.VHostTake(account, vhost, config.Accounts.VHosts.UserRequests.Cooldown)
if err != nil { if err != nil {
if throttled, ok := err.(*vhostThrottleExceeded); ok { if throttled, ok := err.(*vhostThrottleExceeded); ok {
hsNotice(rb, fmt.Sprintf(client.t("You must wait an additional %v before taking a vhost"), throttled.timeRemaining)) hsNotice(rb, fmt.Sprintf(client.t("You must wait an additional %v before taking a vhost"), throttled.timeRemaining))
} else if err == errVhostsForbidden {
hsNotice(rb, client.t("An administrator has denied you the ability to use vhosts"))
} else { } else {
hsNotice(rb, client.t("An error occurred")) hsNotice(rb, client.t("An error occurred"))
} }
} else if vhost != "" {
hsNotice(rb, client.t("Successfully set vhost"))
} else { } else {
hsNotice(rb, client.t("Successfully cleared vhost")) hsNotice(rb, client.t("Successfully set vhost"))
server.snomasks.Send(sno.LocalVhosts, fmt.Sprintf("Client %s (account %s) took vhost %s", client.Nick(), account, vhost))
} }
} }

View File

@ -9,7 +9,7 @@ type Mask rune
// Notice mask types // Notice mask types
const ( const (
LocalAccouncements Mask = 'a' LocalAnnouncements Mask = 'a'
LocalConnects Mask = 'c' LocalConnects Mask = 'c'
LocalChannels Mask = 'j' LocalChannels Mask = 'j'
LocalKills Mask = 'k' LocalKills Mask = 'k'
@ -19,12 +19,13 @@ const (
Stats Mask = 't' Stats Mask = 't'
LocalAccounts Mask = 'u' LocalAccounts Mask = 'u'
LocalXline Mask = 'x' LocalXline Mask = 'x'
LocalVhosts Mask = 'v'
) )
var ( var (
// NoticeMaskNames has readable names for our snomask types. // NoticeMaskNames has readable names for our snomask types.
NoticeMaskNames = map[Mask]string{ NoticeMaskNames = map[Mask]string{
LocalAccouncements: "ANNOUNCEMENT", LocalAnnouncements: "ANNOUNCEMENT",
LocalConnects: "CONNECT", LocalConnects: "CONNECT",
LocalChannels: "CHANNEL", LocalChannels: "CHANNEL",
LocalKills: "KILL", LocalKills: "KILL",
@ -34,11 +35,12 @@ var (
Stats: "STATS", Stats: "STATS",
LocalAccounts: "ACCOUNT", LocalAccounts: "ACCOUNT",
LocalXline: "XLINE", LocalXline: "XLINE",
LocalVhosts: "VHOST",
} }
// ValidMasks contains the snomasks that we support. // ValidMasks contains the snomasks that we support.
ValidMasks = map[Mask]bool{ ValidMasks = map[Mask]bool{
LocalAccouncements: true, LocalAnnouncements: true,
LocalConnects: true, LocalConnects: true,
LocalChannels: true, LocalChannels: true,
LocalKills: true, LocalKills: true,
@ -48,5 +50,6 @@ var (
Stats: true, Stats: true,
LocalAccounts: true, LocalAccounts: true,
LocalXline: true, LocalXline: true,
LocalVhosts: true,
} }
) )

View File

@ -466,7 +466,7 @@ opers:
vhost: "n" vhost: "n"
# modes are the modes to auto-set upon opering-up # modes are the modes to auto-set upon opering-up
modes: +is acjknoqtux modes: +is acjknoqtuxv
# operators can be authenticated either by password (with the /OPER command), # operators can be authenticated either by password (with the /OPER command),
# or by certificate fingerprint, or both. if a password hash is set, then a # or by certificate fingerprint, or both. if a password hash is set, then a