From 25d3a66f91ef1167273fdb6b4e2359ecac005d2a Mon Sep 17 00:00:00 2001 From: binwiederhier Date: Thu, 18 May 2023 13:08:10 -0400 Subject: [PATCH] Upstream access token --- cmd/serve.go | 3 ++ docs/config.md | 6 ++++ docs/install.md | 60 +++++++++++++++++++-------------------- docs/releases.md | 42 ++++++++++++++++----------- server/config.go | 2 ++ server/server.go | 4 +++ server/server.yml | 5 ++++ server/server_test.go | 63 ++++++++++++++++++++++++++++++++++++++++- server/server_twilio.go | 9 ++++-- 9 files changed, 144 insertions(+), 50 deletions(-) diff --git a/cmd/serve.go b/cmd/serve.go index 4e123e9..a326374 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -64,6 +64,7 @@ var flagsServe = append( altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-login", Aliases: []string{"enable_login"}, EnvVars: []string{"NTFY_ENABLE_LOGIN"}, Value: false, Usage: "allows users to log in via the web app, or API"}), altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-reservations", Aliases: []string{"enable_reservations"}, EnvVars: []string{"NTFY_ENABLE_RESERVATIONS"}, Value: false, Usage: "allows users to reserve topics (if their tier allows it)"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "upstream-base-url", Aliases: []string{"upstream_base_url"}, EnvVars: []string{"NTFY_UPSTREAM_BASE_URL"}, Value: "", Usage: "forward poll request to an upstream server, this is needed for iOS push notifications for self-hosted servers"}), + altsrc.NewStringFlag(&cli.StringFlag{Name: "upstream-access-token", Aliases: []string{"upstream_access_token"}, EnvVars: []string{"NTFY_UPSTREAM_ACCESS_TOKEN"}, Value: "", Usage: "access token to use for the upstream server; needed only if upstream rate limits are exceeded or upstream server requires auth"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-addr", Aliases: []string{"smtp_sender_addr"}, EnvVars: []string{"NTFY_SMTP_SENDER_ADDR"}, Usage: "SMTP server address (host:port) for outgoing emails"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-user", Aliases: []string{"smtp_sender_user"}, EnvVars: []string{"NTFY_SMTP_SENDER_USER"}, Usage: "SMTP user (if e-mail sending is enabled)"}), altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-pass", Aliases: []string{"smtp_sender_pass"}, EnvVars: []string{"NTFY_SMTP_SENDER_PASS"}, Usage: "SMTP password (if e-mail sending is enabled)"}), @@ -148,6 +149,7 @@ func execServe(c *cli.Context) error { enableLogin := c.Bool("enable-login") enableReservations := c.Bool("enable-reservations") upstreamBaseURL := c.String("upstream-base-url") + upstreamAccessToken := c.String("upstream-access-token") smtpSenderAddr := c.String("smtp-sender-addr") smtpSenderUser := c.String("smtp-sender-user") smtpSenderPass := c.String("smtp-sender-pass") @@ -311,6 +313,7 @@ func execServe(c *cli.Context) error { conf.DisallowedTopics = disallowedTopics conf.WebRoot = webRoot conf.UpstreamBaseURL = upstreamBaseURL + conf.UpstreamAccessToken = upstreamAccessToken conf.SMTPSenderAddr = smtpSenderAddr conf.SMTPSenderUser = smtpSenderUser conf.SMTPSenderPass = smtpSenderPass diff --git a/docs/config.md b/docs/config.md index df77e9a..66301ee 100644 --- a/docs/config.md +++ b/docs/config.md @@ -759,6 +759,7 @@ To configure it, simply set `upstream-base-url` like so: ``` yaml upstream-base-url: "https://ntfy.sh" +upstream-access-token: "..." # optional, only if rate limits exceeded, or upstream server protected ``` If set, all incoming messages will publish a poll request to the configured upstream server, containing @@ -1258,10 +1259,15 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`). | `smtp-server-listen` | `NTFY_SMTP_SERVER_LISTEN` | `[ip]:port` | - | Defines the IP address and port the SMTP server will listen on, e.g. `:25` or `1.2.3.4:25` | | `smtp-server-domain` | `NTFY_SMTP_SERVER_DOMAIN` | *domain name* | - | SMTP server e-mail domain, e.g. `ntfy.sh` | | `smtp-server-addr-prefix` | `NTFY_SMTP_SERVER_ADDR_PREFIX` | *string* | - | Optional prefix for the e-mail addresses to prevent spam, e.g. `ntfy-` | +| `twilio-account` | `NTFY_TWILIO_ACCOUNT` | *string* | - | Twilio account SID, e.g. AC12345beefbeef67890beefbeef122586 | +| `twilio-auth-token` | `NTFY_TWILIO_AUTH_TOKEN` | *string* | - | Twilio auth token, e.g. affebeef258625862586258625862586 | +| `twilio-from-number` | `NTFY_TWILIO_FROM_NUMBER` | *string* | - | Twilio outgoing phone number, e.g. +18775132586 | +| `twilio-verify-service` | `NTFY_TWILIO_VERIFY_SERVICE` | *string* | - | Twilio Verify service SID, e.g. VA12345beefbeef67890beefbeef122586 | | `keepalive-interval` | `NTFY_KEEPALIVE_INTERVAL` | *duration* | 45s | Interval in which keepalive messages are sent to the client. This is to prevent intermediaries closing the connection for inactivity. Note that the Android app has a hardcoded timeout at 77s, so it should be less than that. | | `manager-interval` | `NTFY_MANAGER_INTERVAL` | *duration* | 1m | Interval in which the manager prunes old messages, deletes topics and prints the stats. | | `global-topic-limit` | `NTFY_GLOBAL_TOPIC_LIMIT` | *number* | 15,000 | Rate limiting: Total number of topics before the server rejects new topics. | | `upstream-base-url` | `NTFY_UPSTREAM_BASE_URL` | *URL* | `https://ntfy.sh` | Forward poll request to an upstream server, this is needed for iOS push notifications for self-hosted servers | +| `upstream-access-token` | `NTFY_UPSTREAM_ACCESS_TOKEN` | *string* | `tk_zyYLYj...` | Access token to use for the upstream server; needed only if upstream rate limits are exceeded or upstream server requires auth | | `visitor-attachment-total-size-limit` | `NTFY_VISITOR_ATTACHMENT_TOTAL_SIZE_LIMIT` | *size* | 100M | Rate limiting: Total storage limit used for attachments per visitor, for all attachments combined. Storage is freed after attachments expire. See `attachment-expiry-duration`. | | `visitor-attachment-daily-bandwidth-limit` | `NTFY_VISITOR_ATTACHMENT_DAILY_BANDWIDTH_LIMIT` | *size* | 500M | Rate limiting: Total daily attachment download/upload traffic limit per visitor. This is to protect your bandwidth costs from exploding. | | `visitor-email-limit-burst` | `NTFY_VISITOR_EMAIL_LIMIT_BURST` | *number* | 16 | Rate limiting:Initial limit of e-mails per visitor | diff --git a/docs/install.md b/docs/install.md index 19522bb..1d28495 100644 --- a/docs/install.md +++ b/docs/install.md @@ -29,37 +29,37 @@ deb/rpm packages. === "x86_64/amd64" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_x86_64.tar.gz - tar zxvf ntfy_2.4.0_linux_x86_64.tar.gz - sudo cp -a ntfy_2.4.0_linux_x86_64/ntfy /usr/local/bin/ntfy - sudo mkdir /etc/ntfy && sudo cp ntfy_2.4.0_linux_x86_64/{client,server}/*.yml /etc/ntfy + wget https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_x86_64.tar.gz + tar zxvf ntfy_2.5.0_linux_x86_64.tar.gz + sudo cp -a ntfy_2.5.0_linux_x86_64/ntfy /usr/local/bin/ntfy + sudo mkdir /etc/ntfy && sudo cp ntfy_2.5.0_linux_x86_64/{client,server}/*.yml /etc/ntfy sudo ntfy serve ``` === "armv6" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_armv6.tar.gz - tar zxvf ntfy_2.4.0_linux_armv6.tar.gz - sudo cp -a ntfy_2.4.0_linux_armv6/ntfy /usr/bin/ntfy - sudo mkdir /etc/ntfy && sudo cp ntfy_2.4.0_linux_armv6/{client,server}/*.yml /etc/ntfy + wget https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_armv6.tar.gz + tar zxvf ntfy_2.5.0_linux_armv6.tar.gz + sudo cp -a ntfy_2.5.0_linux_armv6/ntfy /usr/bin/ntfy + sudo mkdir /etc/ntfy && sudo cp ntfy_2.5.0_linux_armv6/{client,server}/*.yml /etc/ntfy sudo ntfy serve ``` === "armv7/armhf" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_armv7.tar.gz - tar zxvf ntfy_2.4.0_linux_armv7.tar.gz - sudo cp -a ntfy_2.4.0_linux_armv7/ntfy /usr/bin/ntfy - sudo mkdir /etc/ntfy && sudo cp ntfy_2.4.0_linux_armv7/{client,server}/*.yml /etc/ntfy + wget https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_armv7.tar.gz + tar zxvf ntfy_2.5.0_linux_armv7.tar.gz + sudo cp -a ntfy_2.5.0_linux_armv7/ntfy /usr/bin/ntfy + sudo mkdir /etc/ntfy && sudo cp ntfy_2.5.0_linux_armv7/{client,server}/*.yml /etc/ntfy sudo ntfy serve ``` === "arm64" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_arm64.tar.gz - tar zxvf ntfy_2.4.0_linux_arm64.tar.gz - sudo cp -a ntfy_2.4.0_linux_arm64/ntfy /usr/bin/ntfy - sudo mkdir /etc/ntfy && sudo cp ntfy_2.4.0_linux_arm64/{client,server}/*.yml /etc/ntfy + wget https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_arm64.tar.gz + tar zxvf ntfy_2.5.0_linux_arm64.tar.gz + sudo cp -a ntfy_2.5.0_linux_arm64/ntfy /usr/bin/ntfy + sudo mkdir /etc/ntfy && sudo cp ntfy_2.5.0_linux_arm64/{client,server}/*.yml /etc/ntfy sudo ntfy serve ``` @@ -109,7 +109,7 @@ Manually installing the .deb file: === "x86_64/amd64" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_amd64.deb + wget https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_amd64.deb sudo dpkg -i ntfy_*.deb sudo systemctl enable ntfy sudo systemctl start ntfy @@ -117,7 +117,7 @@ Manually installing the .deb file: === "armv6" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_armv6.deb + wget https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_armv6.deb sudo dpkg -i ntfy_*.deb sudo systemctl enable ntfy sudo systemctl start ntfy @@ -125,7 +125,7 @@ Manually installing the .deb file: === "armv7/armhf" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_armv7.deb + wget https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_armv7.deb sudo dpkg -i ntfy_*.deb sudo systemctl enable ntfy sudo systemctl start ntfy @@ -133,7 +133,7 @@ Manually installing the .deb file: === "arm64" ```bash - wget https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_arm64.deb + wget https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_arm64.deb sudo dpkg -i ntfy_*.deb sudo systemctl enable ntfy sudo systemctl start ntfy @@ -143,28 +143,28 @@ Manually installing the .deb file: === "x86_64/amd64" ```bash - sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_amd64.rpm + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_amd64.rpm sudo systemctl enable ntfy sudo systemctl start ntfy ``` === "armv6" ```bash - sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_armv6.rpm + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_armv6.rpm sudo systemctl enable ntfy sudo systemctl start ntfy ``` === "armv7/armhf" ```bash - sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_armv7.rpm + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_armv7.rpm sudo systemctl enable ntfy sudo systemctl start ntfy ``` === "arm64" ```bash - sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_linux_arm64.rpm + sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_linux_arm64.rpm sudo systemctl enable ntfy sudo systemctl start ntfy ``` @@ -192,18 +192,18 @@ NixOS also supports [declarative setup of the ntfy server](https://search.nixos. ## macOS The [ntfy CLI](subscribe/cli.md) (`ntfy publish` and `ntfy subscribe` only) is supported on macOS as well. -To install, please [download the tarball](https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_macOS_all.tar.gz), +To install, please [download the tarball](https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_macOS_all.tar.gz), extract it and place it somewhere in your `PATH` (e.g. `/usr/local/bin/ntfy`). If run as `root`, ntfy will look for its config at `/etc/ntfy/client.yml`. For all other users, it'll look for it at `~/Library/Application Support/ntfy/client.yml` (sample included in the tarball). ```bash -curl -L https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_macOS_all.tar.gz > ntfy_2.4.0_macOS_all.tar.gz -tar zxvf ntfy_2.4.0_macOS_all.tar.gz -sudo cp -a ntfy_2.4.0_macOS_all/ntfy /usr/local/bin/ntfy +curl -L https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_macOS_all.tar.gz > ntfy_2.5.0_macOS_all.tar.gz +tar zxvf ntfy_2.5.0_macOS_all.tar.gz +sudo cp -a ntfy_2.5.0_macOS_all/ntfy /usr/local/bin/ntfy mkdir ~/Library/Application\ Support/ntfy -cp ntfy_2.4.0_macOS_all/client/client.yml ~/Library/Application\ Support/ntfy/client.yml +cp ntfy_2.5.0_macOS_all/client/client.yml ~/Library/Application\ Support/ntfy/client.yml ntfy --help ``` @@ -221,7 +221,7 @@ brew install ntfy ## Windows The [ntfy CLI](subscribe/cli.md) (`ntfy publish` and `ntfy subscribe` only) is supported on Windows as well. -To install, please [download the latest ZIP](https://github.com/binwiederhier/ntfy/releases/download/v2.4.0/ntfy_2.4.0_windows_x86_64.zip), +To install, please [download the latest ZIP](https://github.com/binwiederhier/ntfy/releases/download/v2.5.0/ntfy_2.5.0_windows_x86_64.zip), extract it and place the `ntfy.exe` binary somewhere in your `%Path%`. The default path for the client config file is at `%AppData%\ntfy\client.yml` (not created automatically, sample in the ZIP file). diff --git a/docs/releases.md b/docs/releases.md index 17debf0..c645139 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -2,6 +2,32 @@ Binaries for all releases can be found on the GitHub releases pages for the [ntfy server](https://github.com/binwiederhier/ntfy/releases) and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/releases). +## ntfy server v2.5.0 +Released May 18, 2023 + +This release brings a number of new features, including support for text-to-speech style [phone calls](publish.md#phone-calls), +an admin API to manage users and ACL (currently in beta, and hence undocumented), and support for authorized access to +upstream servers via the `upstream-access-token` config option. + +❤️ If you like ntfy, **please consider sponsoring me** via [GitHub Sponsors](https://github.com/sponsors/binwiederhier) +and [Liberapay](https://en.liberapay.com/ntfy/), or by buying a [paid plan via the web app](https://ntfy.sh/app) (20% off +if you use promo code `MYTOPIC`). ntfy will always remain open source. + +**Features:** + +* Support for text-to-speech style [phone calls](publish.md#phone-calls) using the `X-Call` header (no ticket) +* Admin API to manage users and ACL, `v1/users` + `v1/users/access` (intentionally undocumented as of now, [#722](https://github.com/binwiederhier/ntfy/issues/722), thanks to [@CreativeWarlock](https://github.com/CreativeWarlock) for sponsoring this ticket) +* Added `upstream-access-token` config option to allow authorized access to upstream servers (no ticket) + +**Bug fixes + maintenance:** + +* Removed old ntfy website from ntfy entirely (no ticket) +* Make emoji lookup for emails more efficient ([#725](https://github.com/binwiederhier/ntfy/pull/725), thanks to [@adamantike](https://github.com/adamantike)) +* Fix potential subscriber ID clash ([#712](https://github.com/binwiederhier/ntfy/issues/712), thanks to [@peterbourgon](https://github.com/peterbourgon) for reporting, and [@dropdevrahul](https://github.com/dropdevrahul) for fixing) +* Support for `quoted-printable` in incoming emails ([#719](https://github.com/binwiederhier/ntfy/pull/719), thanks to [@Aerion](https://github.com/Aerion)) +* Attachments with filenames that are downloaded using a browser will now download with the proper filename ([#726](https://github.com/binwiederhier/ntfy/issues/726), thanks to [@un99known99](https://github.com/un99known99) for reporting, and [@wunter8](https://github.com/wunter8) for fixing) +* Fix web app i18n issue in account preferences ([#730](https://github.com/binwiederhier/ntfy/issues/730), thanks to [@codebude](https://github.com/codebude) for reporting) + ### ntfy server v2.4.0 Released Apr 26, 2023 @@ -1178,22 +1204,6 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release ## Not released yet -### ntfy server v2.5.0 (UNRELEASED) - -**Features:** - -* Support for text-to-speech style [phone calls](publish.md#phone-calls) using the `X-Call` header (no ticket) -* Admin API to manage users and ACL, `v1/users` + `v1/users/access` ([#722](https://github.com/binwiederhier/ntfy/issues/722), thanks to [@CreativeWarlock](https://github.com/CreativeWarlock) for sponsoring this ticket) - -**Bug fixes + maintenance:** - -* Removed old ntfy website from ntfy entirely (no ticket) -* Make emoji lookup for emails more efficient ([#725](https://github.com/binwiederhier/ntfy/pull/725), thanks to [@adamantike](https://github.com/adamantike)) -* Fix potential subscriber ID clash ([#712](https://github.com/binwiederhier/ntfy/issues/712), thanks to [@peterbourgon](https://github.com/peterbourgon) for reporting, and [@dropdevrahul](https://github.com/dropdevrahul) for fixing) -* Support for `quoted-printable` in incoming emails ([#719](https://github.com/binwiederhier/ntfy/pull/719), thanks to [@Aerion](https://github.com/Aerion)) -* Attachments with filenames that are downloaded using a browser will now download with the proper filename ([#726](https://github.com/binwiederhier/ntfy/issues/726), thanks to [@un99known99](https://github.com/un99known99) for reporting, and [@wunter8](https://github.com/wunter8) for fixing) -* Fix web app i18n issue in account preferences ([#730](https://github.com/binwiederhier/ntfy/issues/730), thanks to [@codebude](https://github.com/codebude) for reporting) - ### ntfy Android app v1.16.1 (UNRELEASED) **Features:** diff --git a/server/config.go b/server/config.go index 376862a..ae96271 100644 --- a/server/config.go +++ b/server/config.go @@ -98,6 +98,7 @@ type Config struct { FirebasePollInterval time.Duration FirebaseQuotaExceededPenaltyDuration time.Duration UpstreamBaseURL string + UpstreamAccessToken string SMTPSenderAddr string SMTPSenderUser string SMTPSenderPass string @@ -182,6 +183,7 @@ func NewConfig() *Config { FirebasePollInterval: DefaultFirebasePollInterval, FirebaseQuotaExceededPenaltyDuration: DefaultFirebaseQuotaExceededPenaltyDuration, UpstreamBaseURL: "", + UpstreamAccessToken: "", SMTPSenderAddr: "", SMTPSenderUser: "", SMTPSenderPass: "", diff --git a/server/server.go b/server/server.go index 7e8ea25..a451baa 100644 --- a/server/server.go +++ b/server/server.go @@ -855,7 +855,11 @@ func (s *Server) forwardPollRequest(v *visitor, m *message) { logvm(v, m).Err(err).Warn("Unable to publish poll request") return } + req.Header.Set("User-Agent", "ntfy/"+s.config.Version) req.Header.Set("X-Poll-ID", m.ID) + if s.config.UpstreamAccessToken != "" { + req.Header.Set("Authorization", util.BearerAuth(s.config.UpstreamAccessToken)) + } var httpClient = &http.Client{ Timeout: time.Second * 10, } diff --git a/server/server.yml b/server/server.yml index 7484113..2f16fc8 100644 --- a/server/server.yml +++ b/server/server.yml @@ -208,7 +208,12 @@ # the message ID of the original message, instructing the iOS app to poll this server for the actual message contents. # This is to prevent the upstream server and Firebase/APNS from being able to read the message. # +# - upstream-base-url is the base URL of the upstream server. Should be "https://ntfy.sh". +# - upstream-access-token is the token used to authenticate with the upstream server. This is only required +# if you exceed the upstream rate limits, or the uptream server requires authentication. +# # upstream-base-url: +# upstream-access-token: # Rate limiting: Total number of topics before the server rejects new topics. # diff --git a/server/server_test.go b/server/server_test.go index 5725141..0cdb490 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -18,6 +18,7 @@ import ( "runtime/debug" "strings" "sync" + "sync/atomic" "testing" "time" @@ -2491,6 +2492,66 @@ func TestServer_PublishWithUTF8MimeHeader(t *testing.T) { require.Equal(t, "ntfy 很棒", m.Tags[1]) } +func TestServer_UpstreamBaseURL_Success(t *testing.T) { + var pollID atomic.Pointer[string] + upstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + require.Nil(t, err) + require.Equal(t, "/87c9cddf7b0105f5fe849bf084c6e600be0fde99be3223335199b4965bd7b735", r.URL.Path) + require.Equal(t, "", string(body)) + require.NotEmpty(t, r.Header.Get("X-Poll-ID")) + pollID.Store(util.String(r.Header.Get("X-Poll-ID"))) + })) + defer upstreamServer.Close() + + c := newTestConfigWithAuthFile(t) + c.BaseURL = "http://myserver.internal" + c.UpstreamBaseURL = upstreamServer.URL + s := newTestServer(t, c) + + // Send message, and wait for upstream server to receive it + response := request(t, s, "PUT", "/mytopic", `hi there`, nil) + require.Equal(t, 200, response.Code) + m := toMessage(t, response.Body.String()) + require.NotEmpty(t, m.ID) + require.Equal(t, "hi there", m.Message) + waitFor(t, func() bool { + pID := pollID.Load() + return pID != nil && *pID == m.ID + }) +} + +func TestServer_UpstreamBaseURL_With_Access_Token_Success(t *testing.T) { + var pollID atomic.Pointer[string] + upstreamServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + require.Nil(t, err) + require.Equal(t, "/a1c72bcb4daf5af54d13ef86aea8f76c11e8b88320d55f1811d5d7b173bcc1df", r.URL.Path) + require.Equal(t, "Bearer tk_1234567890", r.Header.Get("Authorization")) + require.Equal(t, "", string(body)) + require.NotEmpty(t, r.Header.Get("X-Poll-ID")) + pollID.Store(util.String(r.Header.Get("X-Poll-ID"))) + })) + defer upstreamServer.Close() + + c := newTestConfigWithAuthFile(t) + c.BaseURL = "http://myserver.internal" + c.UpstreamBaseURL = upstreamServer.URL + c.UpstreamAccessToken = "tk_1234567890" + s := newTestServer(t, c) + + // Send message, and wait for upstream server to receive it + response := request(t, s, "PUT", "/mytopic1", `hi there`, nil) + require.Equal(t, 200, response.Code) + m := toMessage(t, response.Body.String()) + require.NotEmpty(t, m.ID) + require.Equal(t, "hi there", m.Message) + waitFor(t, func() bool { + pID := pollID.Load() + return pID != nil && *pID == m.ID + }) +} + func newTestConfig(t *testing.T) *Config { conf := NewConfig() conf.BaseURL = "http://127.0.0.1:12345" @@ -2592,7 +2653,7 @@ func waitForWithMaxWait(t *testing.T, maxWait time.Duration, f func() bool) { if f() { return } - time.Sleep(100 * time.Millisecond) + time.Sleep(50 * time.Millisecond) } t.Fatalf("Function f did not succeed after %v: %v", maxWait, string(debug.Stack())) } diff --git a/server/server_twilio.go b/server/server_twilio.go index 0672357..b557edf 100644 --- a/server/server_twilio.go +++ b/server/server_twilio.go @@ -87,8 +87,9 @@ func (s *Server) callPhoneInternal(data url.Values) (string, error) { if err != nil { return "", err } - req.Header.Set("Authorization", util.BasicAuth(s.config.TwilioAccount, s.config.TwilioAuthToken)) + req.Header.Set("User-Agent", "ntfy/"+s.config.Version) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Authorization", util.BasicAuth(s.config.TwilioAccount, s.config.TwilioAuthToken)) resp, err := http.DefaultClient.Do(req) if err != nil { return "", err @@ -110,8 +111,9 @@ func (s *Server) verifyPhoneNumber(v *visitor, r *http.Request, phoneNumber, cha if err != nil { return err } - req.Header.Set("Authorization", util.BasicAuth(s.config.TwilioAccount, s.config.TwilioAuthToken)) + req.Header.Set("User-Agent", "ntfy/"+s.config.Version) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Authorization", util.BasicAuth(s.config.TwilioAccount, s.config.TwilioAuthToken)) resp, err := http.DefaultClient.Do(req) if err != nil { return err @@ -135,8 +137,9 @@ func (s *Server) verifyPhoneNumberCheck(v *visitor, r *http.Request, phoneNumber if err != nil { return err } - req.Header.Set("Authorization", util.BasicAuth(s.config.TwilioAccount, s.config.TwilioAuthToken)) + req.Header.Set("User-Agent", "ntfy/"+s.config.Version) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Authorization", util.BasicAuth(s.config.TwilioAccount, s.config.TwilioAuthToken)) resp, err := http.DefaultClient.Do(req) if err != nil { return err