From e1f56aaee35f672e7b9d0bfcc6f6e9bf832ab134 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sat, 1 Feb 2020 22:19:33 -0500 Subject: [PATCH 1/3] fix #764 --- irc/accounts.go | 29 ++++++++++++++++++++++++----- irc/hostserv.go | 4 ++-- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/irc/accounts.go b/irc/accounts.go index f926e9c1..b75e34b6 100644 --- a/irc/accounts.go +++ b/irc/accounts.go @@ -1169,13 +1169,9 @@ func (vh *VHostInfo) checkThrottle(cooldown time.Duration) (err error) { // callback type implementing the actual business logic of vhost operations 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) { output = input - err = output.checkThrottle(cooldown) - if err != nil { - return - } output.Enabled = true output.ApprovedVHost = vhost return @@ -1205,6 +1201,29 @@ func (am *AccountManager) VHostRequest(account string, vhost string, cooldown ti 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 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) { munger := func(input VHostInfo) (output VHostInfo, err error) { output = input diff --git a/irc/hostserv.go b/irc/hostserv.go index 8cfa9220..6424910b 100644 --- a/irc/hostserv.go +++ b/irc/hostserv.go @@ -298,7 +298,7 @@ func hsSetHandler(server *Server, client *Client, command string, params []strin } // else: command == "del", vhost == "" - _, err := server.accounts.VHostSet(user, vhost, 0) + _, err := server.accounts.VHostSet(user, vhost) if err != nil { hsNotice(rb, client.t("An error occurred")) } else if vhost != "" { @@ -404,7 +404,7 @@ func hsTakeHandler(server *Server, client *Client, command string, params []stri return } - _, err := server.accounts.VHostSet(client.Account(), vhost, config.Accounts.VHosts.UserRequests.Cooldown) + _, err := server.accounts.VHostTake(client.Account(), vhost, config.Accounts.VHosts.UserRequests.Cooldown) if err != nil { if throttled, ok := err.(*vhostThrottleExceeded); ok { hsNotice(rb, fmt.Sprintf(client.t("You must wait an additional %v before taking a vhost"), throttled.timeRemaining)) From 5426c9fdc0a48547d83856726a42da08fc88353d Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sat, 1 Feb 2020 23:51:29 -0500 Subject: [PATCH 2/3] disallow TAKE and REQUEST while under a FORBID --- irc/accounts.go | 9 ++++++++- irc/errors.go | 1 + irc/hostserv.go | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/irc/accounts.go b/irc/accounts.go index b75e34b6..67e3d0c5 100644 --- a/irc/accounts.go +++ b/irc/accounts.go @@ -1183,6 +1183,10 @@ func (am *AccountManager) VHostSet(account string, vhost string) (result VHostIn func (am *AccountManager) VHostRequest(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 + } // you can update your existing request, but if you were approved or rejected, // you can't spam a new request if output.RequestedVHost == "" { @@ -1204,7 +1208,10 @@ func (am *AccountManager) VHostRequest(account string, vhost string, cooldown ti 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 == "" { diff --git a/irc/errors.go b/irc/errors.go index 95690527..a3a32c6e 100644 --- a/irc/errors.go +++ b/irc/errors.go @@ -50,6 +50,7 @@ var ( errBanned = errors.New("IP or nickmask banned") errInvalidParams = utils.ErrInvalidParams 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") errNoop = errors.New("Action was a no-op") errCASFailed = errors.New("Compare-and-swap update of database value failed") diff --git a/irc/hostserv.go b/irc/hostserv.go index 6424910b..0a1371a9 100644 --- a/irc/hostserv.go +++ b/irc/hostserv.go @@ -216,6 +216,8 @@ func hsRequestHandler(server *Server, client *Client, command string, params []s if err != nil { if throttled, ok := err.(*vhostThrottleExceeded); ok { 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 { hsNotice(rb, client.t("An error occurred")) } @@ -408,6 +410,8 @@ func hsTakeHandler(server *Server, client *Client, command string, params []stri if err != nil { if throttled, ok := err.(*vhostThrottleExceeded); ok { 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 { hsNotice(rb, client.t("An error occurred")) } From 88d7e98011cfbe009636cbed807f14367c649f4c Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 2 Feb 2020 02:03:08 -0500 Subject: [PATCH 3/3] add vhost snomasks --- irc/hostserv.go | 14 +++++++++----- irc/sno/constants.go | 9 ++++++--- oragono.yaml | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/irc/hostserv.go b/irc/hostserv.go index 0a1371a9..1b7c4c2b 100644 --- a/irc/hostserv.go +++ b/irc/hostserv.go @@ -7,6 +7,8 @@ import ( "errors" "fmt" "regexp" + + "github.com/oragono/oragono/irc/sno" ) const hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed @@ -225,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")) chanMsg := fmt.Sprintf("Account %s requests vhost %s", accountName, vhost) hsNotifyChannel(server, chanMsg) - // TODO send admins a snomask of some kind + server.snomasks.Send(sno.LocalVhosts, chanMsg) } } @@ -328,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)) chanMsg := fmt.Sprintf("Oper %[1]s approved vhost %[2]s for account %[3]s", client.Nick(), vhostInfo.ApprovedVHost, user) hsNotifyChannel(server, chanMsg) + server.snomasks.Send(sno.LocalVhosts, chanMsg) for _, client := range server.accounts.AccountToClients(user) { client.Notice(client.t("Your vhost request was approved by an administrator")) } @@ -348,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)) chanMsg := fmt.Sprintf("Oper %s rejected vhost %s for account %s, with the reason: %v", client.Nick(), vhostInfo.RejectedVHost, user, reason) hsNotifyChannel(server, chanMsg) + server.snomasks.Send(sno.LocalVhosts, chanMsg) for _, client := range server.accounts.AccountToClients(user) { if reason == "" { client.Notice("Your vhost request was rejected by an administrator") @@ -406,7 +410,8 @@ func hsTakeHandler(server *Server, client *Client, command string, params []stri return } - _, err := server.accounts.VHostTake(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 throttled, ok := err.(*vhostThrottleExceeded); ok { hsNotice(rb, fmt.Sprintf(client.t("You must wait an additional %v before taking a vhost"), throttled.timeRemaining)) @@ -415,9 +420,8 @@ func hsTakeHandler(server *Server, client *Client, command string, params []stri } else { hsNotice(rb, client.t("An error occurred")) } - } else if vhost != "" { - hsNotice(rb, client.t("Successfully set vhost")) } 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)) } } diff --git a/irc/sno/constants.go b/irc/sno/constants.go index 5449962e..1eb1bc5a 100644 --- a/irc/sno/constants.go +++ b/irc/sno/constants.go @@ -9,7 +9,7 @@ type Mask rune // Notice mask types const ( - LocalAccouncements Mask = 'a' + LocalAnnouncements Mask = 'a' LocalConnects Mask = 'c' LocalChannels Mask = 'j' LocalKills Mask = 'k' @@ -19,12 +19,13 @@ const ( Stats Mask = 't' LocalAccounts Mask = 'u' LocalXline Mask = 'x' + LocalVhosts Mask = 'v' ) var ( // NoticeMaskNames has readable names for our snomask types. NoticeMaskNames = map[Mask]string{ - LocalAccouncements: "ANNOUNCEMENT", + LocalAnnouncements: "ANNOUNCEMENT", LocalConnects: "CONNECT", LocalChannels: "CHANNEL", LocalKills: "KILL", @@ -34,11 +35,12 @@ var ( Stats: "STATS", LocalAccounts: "ACCOUNT", LocalXline: "XLINE", + LocalVhosts: "VHOST", } // ValidMasks contains the snomasks that we support. ValidMasks = map[Mask]bool{ - LocalAccouncements: true, + LocalAnnouncements: true, LocalConnects: true, LocalChannels: true, LocalKills: true, @@ -48,5 +50,6 @@ var ( Stats: true, LocalAccounts: true, LocalXline: true, + LocalVhosts: true, } ) diff --git a/oragono.yaml b/oragono.yaml index 83a29915..54131ad2 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -466,7 +466,7 @@ opers: vhost: "n" # 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), # or by certificate fingerprint, or both. if a password hash is set, then a