From 4b9e0c5c3817ca4ed716dab6f12f473e168eaa15 Mon Sep 17 00:00:00 2001 From: binwiederhier Date: Mon, 15 May 2023 20:42:43 -0400 Subject: [PATCH] Phone number verification in publishing --- server/errors.go | 4 +++- server/server.go | 16 +++++++--------- server/server_twilio.go | 21 +++++++++++++++++---- web/src/components/Account.js | 8 ++++++++ 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/server/errors.go b/server/errors.go index ee5223bf..a42641b4 100644 --- a/server/errors.go +++ b/server/errors.go @@ -108,8 +108,10 @@ var ( errHTTPBadRequestBillingSubscriptionExists = &errHTTP{40029, http.StatusBadRequest, "invalid request: billing subscription already exists", "", nil} errHTTPBadRequestTierInvalid = &errHTTP{40030, http.StatusBadRequest, "invalid request: tier does not exist", "", nil} errHTTPBadRequestUserNotFound = &errHTTP{40031, http.StatusBadRequest, "invalid request: user does not exist", "", nil} - errHTTPBadRequestTwilioDisabled = &errHTTP{40032, http.StatusBadRequest, "invalid request: Calling is disabled", "https://ntfy.sh/docs/publish/#phone-calls", nil} + errHTTPBadRequestPhoneCallsDisabled = &errHTTP{40032, http.StatusBadRequest, "invalid request: calling is disabled", "https://ntfy.sh/docs/publish/#phone-calls", nil} errHTTPBadRequestPhoneNumberInvalid = &errHTTP{40033, http.StatusBadRequest, "invalid request: phone number invalid", "https://ntfy.sh/docs/publish/#phone-calls", nil} + errHTTPBadRequestPhoneNumberNotVerified = &errHTTP{40034, http.StatusBadRequest, "invalid request: phone number not verified, or no matching verified numbers found", "https://ntfy.sh/docs/publish/#phone-calls", nil} + errHTTPBadRequestAnonymousCallsNotAllowed = &errHTTP{40035, http.StatusBadRequest, "invalid request: anonymous phone calls are not allowed", "https://ntfy.sh/docs/publish/#phone-calls", nil} errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", "", nil} errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication", nil} errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication", nil} diff --git a/server/server.go b/server/server.go index 430fa5cb..08cf08d3 100644 --- a/server/server.go +++ b/server/server.go @@ -707,17 +707,14 @@ func (s *Server) handlePublishInternal(r *http.Request, v *visitor) (*message, e } else if email != "" && !vrate.EmailAllowed() { return nil, errHTTPTooManyRequestsLimitEmails.With(t) } else if call != "" { - call, err = s.convertPhoneNumber(v.User(), call) - if err != nil { - return nil, errHTTPBadRequestInvalidPhoneNumber.With(t) - } - if !vrate.CallAllowed() { + var httpErr *errHTTP + call, httpErr = s.convertPhoneNumber(v.User(), call) + if httpErr != nil { + return nil, httpErr.With(t) + } else if !vrate.CallAllowed() { return nil, errHTTPTooManyRequestsLimitCalls.With(t) } } - - // FIXME check allowed phone numbers - if m.PollID != "" { m = newPollRequestMessage(t.ID, m.PollID) } @@ -741,6 +738,7 @@ func (s *Server) handlePublishInternal(r *http.Request, v *visitor) (*message, e "message_firebase": firebase, "message_unifiedpush": unifiedpush, "message_email": email, + "message_call": call, }) if ev.IsTrace() { ev.Field("message_body", util.MaybeMarshalJSON(m)).Trace("Received message") @@ -913,7 +911,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 + return false, false, "", "", false, errHTTPBadRequestPhoneCallsDisabled } else if call != "" && !isBoolValue(call) && !phoneNumberRegex.MatchString(call) { return false, false, "", "", false, errHTTPBadRequestPhoneNumberInvalid } diff --git a/server/server_twilio.go b/server/server_twilio.go index 128ae5ef..2c3d0a3e 100644 --- a/server/server_twilio.go +++ b/server/server_twilio.go @@ -31,14 +31,27 @@ const ( ` ) -func (s *Server) convertPhoneNumber(u *user.User, phoneNumber string) (string, error) { +func (s *Server) convertPhoneNumber(u *user.User, phoneNumber string) (string, *errHTTP) { if u == nil { - return "", fmt.Errorf("user is nil") + return "", errHTTPBadRequestAnonymousCallsNotAllowed } - if s.config.TwilioPhoneNumberConverter == nil { + phoneNumbers, err := s.userManager.PhoneNumbers(u.ID) + if err != nil { + return "", errHTTPInternalError + } else if len(phoneNumbers) == 0 { + return "", errHTTPBadRequestPhoneNumberNotVerified + } + if toBool(phoneNumber) { + return phoneNumbers[0], nil + } else if util.Contains(phoneNumbers, phoneNumber) { return phoneNumber, nil } - return s.config.TwilioPhoneNumberConverter(u, phoneNumber) + for _, p := range phoneNumbers { + if p == phoneNumber { + return phoneNumber, nil + } + } + return "", errHTTPBadRequestPhoneNumberNotVerified } func (s *Server) callPhone(v *visitor, r *http.Request, m *message, to string) { diff --git a/web/src/components/Account.js b/web/src/components/Account.js index 706ac02a..28d24a38 100644 --- a/web/src/components/Account.js +++ b/web/src/components/Account.js @@ -359,6 +359,14 @@ const PhoneNumbers = () => { return null; } + if (account?.limits.calls === 0) { + return ( + {t("account_basics_phone_numbers_title")}{config.enable_payments && }} description={t("account_basics_phone_numbers_description")}> + {t("account_usage_calls_none")} + + ) + } + return (