Skip to content

Commit b80aec9

Browse files
committed
1 parent 140fdcc commit b80aec9

File tree

7 files changed

+95
-15
lines changed

7 files changed

+95
-15
lines changed

client/client.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77

88
# Default credentials will be used with "ntfy publish" and "ntfy subscribe" if no other credentials are provided.
99
# You can set a default token to use or a default user:password combination, but not both. For an empty password,
10-
# use empty double-quotes ("")
10+
# use empty double-quotes ("").
11+
#
12+
# To override the default user:password combination or default token for a particular subscription (e.g., to send
13+
# no Authorization header), set the user:pass/token for the subscription to empty double-quotes ("").
1114

1215
# default-token:
1316

client/config.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ type Config struct {
2323
// Subscribe is the struct for a Subscription within Config
2424
type Subscribe struct {
2525
Topic string `yaml:"topic"`
26-
User string `yaml:"user"`
26+
User *string `yaml:"user"`
2727
Password *string `yaml:"password"`
28-
Token string `yaml:"token"`
28+
Token *string `yaml:"token"`
2929
Command string `yaml:"command"`
3030
If map[string]string `yaml:"if"`
3131
}

client/config_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ subscribe:
3737
require.Equal(t, 4, len(conf.Subscribe))
3838
require.Equal(t, "no-command-with-auth", conf.Subscribe[0].Topic)
3939
require.Equal(t, "", conf.Subscribe[0].Command)
40-
require.Equal(t, "phil", conf.Subscribe[0].User)
40+
require.Equal(t, "phil", *conf.Subscribe[0].User)
4141
require.Equal(t, "mypass", *conf.Subscribe[0].Password)
4242
require.Equal(t, "echo-this", conf.Subscribe[1].Topic)
4343
require.Equal(t, `echo "Message received: $message"`, conf.Subscribe[1].Command)
@@ -67,7 +67,7 @@ subscribe:
6767
require.Equal(t, 1, len(conf.Subscribe))
6868
require.Equal(t, "no-command-with-auth", conf.Subscribe[0].Topic)
6969
require.Equal(t, "", conf.Subscribe[0].Command)
70-
require.Equal(t, "phil", conf.Subscribe[0].User)
70+
require.Equal(t, "phil", *conf.Subscribe[0].User)
7171
require.Equal(t, "", *conf.Subscribe[0].Password)
7272
}
7373

@@ -91,7 +91,7 @@ subscribe:
9191
require.Equal(t, 1, len(conf.Subscribe))
9292
require.Equal(t, "no-command-with-auth", conf.Subscribe[0].Topic)
9393
require.Equal(t, "", conf.Subscribe[0].Command)
94-
require.Equal(t, "phil", conf.Subscribe[0].User)
94+
require.Equal(t, "phil", *conf.Subscribe[0].User)
9595
require.Nil(t, conf.Subscribe[0].Password)
9696
}
9797

@@ -113,7 +113,7 @@ subscribe:
113113
require.Equal(t, 1, len(conf.Subscribe))
114114
require.Equal(t, "no-command-with-auth", conf.Subscribe[0].Topic)
115115
require.Equal(t, "", conf.Subscribe[0].Command)
116-
require.Equal(t, "phil", conf.Subscribe[0].User)
116+
require.Equal(t, "phil", *conf.Subscribe[0].User)
117117
require.Nil(t, conf.Subscribe[0].Password)
118118
}
119119

@@ -134,7 +134,7 @@ subscribe:
134134
require.Equal(t, "tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2", conf.DefaultToken)
135135
require.Equal(t, 1, len(conf.Subscribe))
136136
require.Equal(t, "mytopic", conf.Subscribe[0].Topic)
137-
require.Equal(t, "", conf.Subscribe[0].User)
137+
require.Nil(t, conf.Subscribe[0].User)
138138
require.Nil(t, conf.Subscribe[0].Password)
139-
require.Equal(t, "", conf.Subscribe[0].Token)
139+
require.Nil(t, conf.Subscribe[0].Token)
140140
}

client/options.go

+15
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ func WithBearerAuth(token string) PublishOption {
9797
return WithHeader("Authorization", fmt.Sprintf("Bearer %s", token))
9898
}
9999

100+
// WithEmptyAuth clears the Authorization header
101+
func WithEmptyAuth() PublishOption {
102+
return RemoveHeader("Authorization")
103+
}
104+
100105
// WithNoCache instructs the server not to cache the message server-side
101106
func WithNoCache() PublishOption {
102107
return WithHeader("X-Cache", "no")
@@ -187,3 +192,13 @@ func WithQueryParam(param, value string) RequestOption {
187192
return nil
188193
}
189194
}
195+
196+
// RemoveHeader is a generic option to remove a header from a request
197+
func RemoveHeader(header string) RequestOption {
198+
return func(r *http.Request) error {
199+
if header != "" {
200+
delete(r.Header, header)
201+
}
202+
return nil
203+
}
204+
}

cmd/subscribe.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,17 @@ func doSubscribe(c *cli.Context, cl *client.Client, conf *client.Config, topic,
225225
}
226226

227227
func maybeAddAuthHeader(s client.Subscribe, conf *client.Config) client.SubscribeOption {
228+
// if an explicit empty token or empty user:pass is given, exit without auth
229+
if (s.Token != nil && *s.Token == "") || (s.User != nil && *s.User == "" && s.Password != nil && *s.Password == "") {
230+
return client.WithEmptyAuth()
231+
}
232+
228233
// check for subscription token then subscription user:pass
229-
if s.Token != "" {
230-
return client.WithBearerAuth(s.Token)
234+
if s.Token != nil && *s.Token != "" {
235+
return client.WithBearerAuth(*s.Token)
231236
}
232-
if s.User != "" && s.Password != nil {
233-
return client.WithBasicAuth(s.User, *s.Password)
237+
if s.User != nil && *s.User != "" && s.Password != nil {
238+
return client.WithBasicAuth(*s.User, *s.Password)
234239
}
235240

236241
// if no subscription token nor subscription user:pass, check for default token then default user:pass

cmd/subscribe_test.go

+58-2
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ default-token: tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2
330330

331331
app, _, stdout, _ := newTestApp()
332332

333-
require.Nil(t, app.Run([]string{"ntfy", "subscribe", "--poll", "--config=" + filename, "mytopic"}))
333+
require.Nil(t, app.Run([]string{"ntfy", "subscribe", "--poll", "--from-config", "--config=" + filename, "mytopic"}))
334334

335335
require.Equal(t, message, strings.TrimSpace(stdout.String()))
336336
}
@@ -355,7 +355,63 @@ default-password: mypass
355355

356356
app, _, stdout, _ := newTestApp()
357357

358-
require.Nil(t, app.Run([]string{"ntfy", "subscribe", "--poll", "--config=" + filename, "mytopic"}))
358+
require.Nil(t, app.Run([]string{"ntfy", "subscribe", "--poll", "--from-config", "--config=" + filename, "mytopic"}))
359+
360+
require.Equal(t, message, strings.TrimSpace(stdout.String()))
361+
}
362+
363+
func TestCLI_Subscribe_Override_Default_UserPass_With_Empty_UserPass(t *testing.T) {
364+
message := `{"id":"RXIQBFaieLVr","time":124,"expires":1124,"event":"message","topic":"mytopic","message":"triggered"}`
365+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
366+
require.Equal(t, "/mytopic/json", r.URL.Path)
367+
require.Equal(t, "", r.Header.Get("Authorization"))
368+
369+
w.WriteHeader(http.StatusOK)
370+
w.Write([]byte(message))
371+
}))
372+
defer server.Close()
373+
374+
filename := filepath.Join(t.TempDir(), "client.yml")
375+
require.Nil(t, os.WriteFile(filename, []byte(fmt.Sprintf(`
376+
default-host: %s
377+
default-user: philipp
378+
default-password: mypass
379+
subscribe:
380+
- topic: mytopic
381+
user: ""
382+
password: ""
383+
`, server.URL)), 0600))
384+
385+
app, _, stdout, _ := newTestApp()
386+
387+
require.Nil(t, app.Run([]string{"ntfy", "subscribe", "--poll", "--from-config", "--config=" + filename}))
388+
389+
require.Equal(t, message, strings.TrimSpace(stdout.String()))
390+
}
391+
392+
func TestCLI_Subscribe_Override_Default_Token_With_Empty_Token(t *testing.T) {
393+
message := `{"id":"RXIQBFaieLVr","time":124,"expires":1124,"event":"message","topic":"mytopic","message":"triggered"}`
394+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
395+
require.Equal(t, "/mytopic/json", r.URL.Path)
396+
require.Equal(t, "", r.Header.Get("Authorization"))
397+
398+
w.WriteHeader(http.StatusOK)
399+
w.Write([]byte(message))
400+
}))
401+
defer server.Close()
402+
403+
filename := filepath.Join(t.TempDir(), "client.yml")
404+
require.Nil(t, os.WriteFile(filename, []byte(fmt.Sprintf(`
405+
default-host: %s
406+
default-token: tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2
407+
subscribe:
408+
- topic: mytopic
409+
token: ""
410+
`, server.URL)), 0600))
411+
412+
app, _, stdout, _ := newTestApp()
413+
414+
require.Nil(t, app.Run([]string{"ntfy", "subscribe", "--poll", "--from-config", "--config=" + filename}))
359415

360416
require.Equal(t, message, strings.TrimSpace(stdout.String()))
361417
}

docs/releases.md

+1
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
12621262

12631263
* Fix issues with date/time with different locales ([#700](https://github.com/binwiederhier/ntfy/issues/700), thanks to [@nimbleghost](https://github.com/nimbleghost))
12641264
* Re-init i18n on each service worker message to avoid missing translations ([#817](https://github.com/binwiederhier/ntfy/pull/817), thanks to [@nihalgonsalves](https://github.com/nihalgonsalves))
1265+
* You can now unset the default user:pass/token in `client.yml` for an individual subscription to remove the Authorization header ([#829](https://github.com/binwiederhier/ntfy/issues/829), thanks to [tomeon](https://github.com/tomeon) for reporting and to [@wunter8](https://github.com/wunter8) for fixing)
12651266

12661267
### ntfy Android app v1.16.1 (UNRELEASED)
12671268

0 commit comments

Comments
 (0)