diff --git a/cmd/serve.go b/cmd/serve.go index 9e02057..28081a0 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -84,7 +84,6 @@ var flagsServe = append( altsrc.NewStringFlag(&cli.StringFlag{Name: "visitor-request-limit-exempt-hosts", Aliases: []string{"visitor_request_limit_exempt_hosts"}, EnvVars: []string{"NTFY_VISITOR_REQUEST_LIMIT_EXEMPT_HOSTS"}, Value: "", Usage: "hostnames and/or IP addresses of hosts that will be exempt from the visitor request limit"}), altsrc.NewIntFlag(&cli.IntFlag{Name: "visitor-message-daily-limit", Aliases: []string{"visitor_message_daily_limit"}, EnvVars: []string{"NTFY_VISITOR_MESSAGE_DAILY_LIMIT"}, Value: server.DefaultVisitorMessageDailyLimit, Usage: "max messages per visitor per day, derived from request limit if unset"}), altsrc.NewIntFlag(&cli.IntFlag{Name: "visitor-email-limit-burst", Aliases: []string{"visitor_email_limit_burst"}, EnvVars: []string{"NTFY_VISITOR_EMAIL_LIMIT_BURST"}, Value: server.DefaultVisitorEmailLimitBurst, Usage: "initial limit of e-mails per visitor"}), - altsrc.NewIntFlag(&cli.IntFlag{Name: "visitor-call-daily-limit", Aliases: []string{"visitor_call_daily_limit"}, EnvVars: []string{"NTFY_VISITOR_CALL_DAILY_LIMIT"}, Value: server.DefaultVisitorCallDailyLimit, Usage: "max number of phone calls per visitor per day"}), altsrc.NewDurationFlag(&cli.DurationFlag{Name: "visitor-email-limit-replenish", Aliases: []string{"visitor_email_limit_replenish"}, EnvVars: []string{"NTFY_VISITOR_EMAIL_LIMIT_REPLENISH"}, Value: server.DefaultVisitorEmailLimitReplenish, Usage: "interval at which burst limit is replenished (one per x)"}), altsrc.NewBoolFlag(&cli.BoolFlag{Name: "visitor-subscriber-rate-limiting", Aliases: []string{"visitor_subscriber_rate_limiting"}, EnvVars: []string{"NTFY_VISITOR_SUBSCRIBER_RATE_LIMITING"}, Value: false, Usage: "enables subscriber-based rate limiting"}), altsrc.NewBoolFlag(&cli.BoolFlag{Name: "behind-proxy", Aliases: []string{"behind_proxy", "P"}, EnvVars: []string{"NTFY_BEHIND_PROXY"}, Value: false, Usage: "if set, use X-Forwarded-For header to determine visitor IP address (for rate limiting)"}), @@ -171,7 +170,6 @@ func execServe(c *cli.Context) error { visitorMessageDailyLimit := c.Int("visitor-message-daily-limit") visitorEmailLimitBurst := c.Int("visitor-email-limit-burst") visitorEmailLimitReplenish := c.Duration("visitor-email-limit-replenish") - visitorCallDailyLimit := c.Int("visitor-call-daily-limit") behindProxy := c.Bool("behind-proxy") stripeSecretKey := c.String("stripe-secret-key") stripeWebhookKey := c.String("stripe-webhook-key") @@ -334,7 +332,6 @@ func execServe(c *cli.Context) error { conf.VisitorMessageDailyLimit = visitorMessageDailyLimit conf.VisitorEmailLimitBurst = visitorEmailLimitBurst conf.VisitorEmailLimitReplenish = visitorEmailLimitReplenish - conf.VisitorCallDailyLimit = visitorCallDailyLimit conf.VisitorSubscriberRateLimiting = visitorSubscriberRateLimiting conf.BehindProxy = behindProxy conf.StripeSecretKey = stripeSecretKey diff --git a/server/config.go b/server/config.go index 3fd88e8..80eb613 100644 --- a/server/config.go +++ b/server/config.go @@ -129,7 +129,6 @@ type Config struct { VisitorMessageDailyLimit int VisitorEmailLimitBurst int VisitorEmailLimitReplenish time.Duration - VisitorCallDailyLimit int VisitorAccountCreationLimitBurst int VisitorAccountCreationLimitReplenish time.Duration VisitorAuthFailureLimitBurst int diff --git a/server/server.go b/server/server.go index ce87f97..505de4b 100644 --- a/server/server.go +++ b/server/server.go @@ -691,12 +691,18 @@ func (s *Server) handlePublishInternal(r *http.Request, v *visitor) (*message, e return nil, errHTTPTooManyRequestsLimitMessages.With(t) } else if email != "" && !vrate.EmailAllowed() { return nil, errHTTPTooManyRequestsLimitEmails.With(t) - } else if call != "" && !vrate.CallAllowed() { - return nil, errHTTPTooManyRequestsLimitCalls.With(t) + } else if call != "" { + call, err = s.convertPhoneNumber(v.User(), call) + if err != nil { + return nil, errHTTPBadRequestInvalidPhoneNumber.With(t) + } + if !vrate.CallAllowed() { + return nil, errHTTPTooManyRequestsLimitCalls.With(t) + } } // FIXME check allowed phone numbers - + if m.PollID != "" { m = newPollRequestMessage(t.ID, m.PollID) } @@ -893,7 +899,7 @@ func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, fi call = readParam(r, "x-call", "call") if call != "" && s.config.TwilioAccount == "" { return false, false, "", "", false, errHTTPBadRequestTwilioDisabled - } else if call != "" && !phoneNumberRegex.MatchString(call) { + } else if call != "" && !isBoolValue(call) && !phoneNumberRegex.MatchString(call) { return false, false, "", "", false, errHTTPBadRequestPhoneNumberInvalid } messageStr := strings.ReplaceAll(readParam(r, "x-message", "message", "m"), "\\n", "\n") diff --git a/server/server_twilio.go b/server/server_twilio.go index a6b9109..128ae5e 100644 --- a/server/server_twilio.go +++ b/server/server_twilio.go @@ -31,6 +31,16 @@ const ( ` ) +func (s *Server) convertPhoneNumber(u *user.User, phoneNumber string) (string, error) { + if u == nil { + return "", fmt.Errorf("user is nil") + } + if s.config.TwilioPhoneNumberConverter == nil { + return phoneNumber, nil + } + return s.config.TwilioPhoneNumberConverter(u, phoneNumber) +} + func (s *Server) callPhone(v *visitor, r *http.Request, m *message, to string) { body := fmt.Sprintf(twilioCallFormat, xmlEscapeText(m.Topic), xmlEscapeText(m.Message), xmlEscapeText(s.messageFooter(v.User(), m))) data := url.Values{} diff --git a/server/util.go b/server/util.go index f0b49d2..a3a4554 100644 --- a/server/util.go +++ b/server/util.go @@ -18,6 +18,14 @@ func readBoolParam(r *http.Request, defaultValue bool, names ...string) bool { if value == "" { return defaultValue } + return toBool(value) +} + +func isBoolValue(value string) bool { + return value == "1" || value == "yes" || value == "true" || value == "0" || value == "no" || value == "false" +} + +func toBool(value string) bool { return value == "1" || value == "yes" || value == "true" } diff --git a/server/visitor.go b/server/visitor.go index 4895c3f..e4c06f6 100644 --- a/server/visitor.go +++ b/server/visitor.go @@ -24,6 +24,10 @@ const ( // visitorDefaultReservationsLimit is the amount of topic names a user without a tier is allowed to reserve. // This number is zero, and changing it may have unintended consequences in the web app, or otherwise visitorDefaultReservationsLimit = int64(0) + + // visitorDefaultCallsLimit is the amount of calls a user without a tier is allowed to make. + // This number is zero, because phone numbers have to be verified first. + visitorDefaultCallsLimit = int64(0) ) // Constants used to convert a tier-user's MessageLimit (see user.Tier) into adequate request limiter @@ -444,7 +448,7 @@ func configBasedVisitorLimits(conf *Config) *visitorLimits { EmailLimit: replenishDurationToDailyLimit(conf.VisitorEmailLimitReplenish), // Approximation! EmailLimitBurst: conf.VisitorEmailLimitBurst, EmailLimitReplenish: rate.Every(conf.VisitorEmailLimitReplenish), - CallLimit: int64(conf.VisitorCallDailyLimit), + CallLimit: visitorDefaultCallsLimit, ReservationsLimit: visitorDefaultReservationsLimit, AttachmentTotalSizeLimit: conf.VisitorAttachmentTotalSizeLimit, AttachmentFileSizeLimit: conf.AttachmentFileSizeLimit,