Merge pull request #148 from lrabane/cli-auth

Add authentification support for subscribing with CLI
This commit is contained in:
Philipp C. Heckel 2022-02-17 15:25:18 -05:00 committed by GitHub
commit 43326be637
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 5 deletions

View File

@ -16,6 +16,10 @@
# command: 'echo "$message"' # command: 'echo "$message"'
# if: # if:
# priority: high,urgent # priority: high,urgent
# - topic: secret
# command: 'notify-send "$m"'
# user: phill
# password: mypass
# #
# Variables: # Variables:
# Variable Aliases Description # Variable Aliases Description

View File

@ -14,9 +14,11 @@ const (
type Config struct { type Config struct {
DefaultHost string `yaml:"default-host"` DefaultHost string `yaml:"default-host"`
Subscribe []struct { Subscribe []struct {
Topic string `yaml:"topic"` Topic string `yaml:"topic"`
Command string `yaml:"command"` User string `yaml:"user"`
If map[string]string `yaml:"if"` Password string `yaml:"password"`
Command string `yaml:"command"`
If map[string]string `yaml:"if"`
} `yaml:"subscribe"` } `yaml:"subscribe"`
} }

View File

@ -13,7 +13,9 @@ func TestConfig_Load(t *testing.T) {
require.Nil(t, os.WriteFile(filename, []byte(` require.Nil(t, os.WriteFile(filename, []byte(`
default-host: http://localhost default-host: http://localhost
subscribe: subscribe:
- topic: no-command - topic: no-command-with-auth
user: phil
password: mypass
- topic: echo-this - topic: echo-this
command: 'echo "Message received: $message"' command: 'echo "Message received: $message"'
- topic: alerts - topic: alerts
@ -26,8 +28,10 @@ subscribe:
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, "http://localhost", conf.DefaultHost) require.Equal(t, "http://localhost", conf.DefaultHost)
require.Equal(t, 3, len(conf.Subscribe)) require.Equal(t, 3, len(conf.Subscribe))
require.Equal(t, "no-command", conf.Subscribe[0].Topic) require.Equal(t, "no-command-with-auth", conf.Subscribe[0].Topic)
require.Equal(t, "", conf.Subscribe[0].Command) require.Equal(t, "", conf.Subscribe[0].Command)
require.Equal(t, "phil", conf.Subscribe[0].User)
require.Equal(t, "mypass", conf.Subscribe[0].Password)
require.Equal(t, "echo-this", conf.Subscribe[1].Topic) require.Equal(t, "echo-this", conf.Subscribe[1].Topic)
require.Equal(t, `echo "Message received: $message"`, conf.Subscribe[1].Command) require.Equal(t, `echo "Message received: $message"`, conf.Subscribe[1].Command)
require.Equal(t, "alerts", conf.Subscribe[2].Topic) require.Equal(t, "alerts", conf.Subscribe[2].Topic)

View File

@ -23,6 +23,7 @@ var cmdSubscribe = &cli.Command{
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{Name: "config", Aliases: []string{"c"}, Usage: "client config file"}, &cli.StringFlag{Name: "config", Aliases: []string{"c"}, Usage: "client config file"},
&cli.StringFlag{Name: "since", Aliases: []string{"s"}, Usage: "return events since `SINCE` (Unix timestamp, or all)"}, &cli.StringFlag{Name: "since", Aliases: []string{"s"}, Usage: "return events since `SINCE` (Unix timestamp, or all)"},
&cli.StringFlag{Name: "user", Aliases: []string{"u"}, Usage: "username[:password] used to auth against the server"},
&cli.BoolFlag{Name: "from-config", Aliases: []string{"C"}, Usage: "read subscriptions from config file (service mode)"}, &cli.BoolFlag{Name: "from-config", Aliases: []string{"C"}, Usage: "read subscriptions from config file (service mode)"},
&cli.BoolFlag{Name: "poll", Aliases: []string{"p"}, Usage: "return events and exit, do not listen for new events"}, &cli.BoolFlag{Name: "poll", Aliases: []string{"p"}, Usage: "return events and exit, do not listen for new events"},
&cli.BoolFlag{Name: "scheduled", Aliases: []string{"sched", "S"}, Usage: "also return scheduled/delayed events"}, &cli.BoolFlag{Name: "scheduled", Aliases: []string{"sched", "S"}, Usage: "also return scheduled/delayed events"},
@ -40,6 +41,7 @@ ntfy subscribe TOPIC
ntfy subscribe mytopic # Prints JSON for incoming messages for ntfy.sh/mytopic ntfy subscribe mytopic # Prints JSON for incoming messages for ntfy.sh/mytopic
ntfy sub home.lan/backups # Subscribe to topic on different server ntfy sub home.lan/backups # Subscribe to topic on different server
ntfy sub --poll home.lan/backups # Just query for latest messages and exit ntfy sub --poll home.lan/backups # Just query for latest messages and exit
ntfy sub -u phil:mypass secret # Subscribe with username/password
ntfy subscribe TOPIC COMMAND ntfy subscribe TOPIC COMMAND
This executes COMMAND for every incoming messages. The message fields are passed to the This executes COMMAND for every incoming messages. The message fields are passed to the
@ -81,6 +83,7 @@ func execSubscribe(c *cli.Context) error {
} }
cl := client.New(conf) cl := client.New(conf)
since := c.String("since") since := c.String("since")
user := c.String("user")
poll := c.Bool("poll") poll := c.Bool("poll")
scheduled := c.Bool("scheduled") scheduled := c.Bool("scheduled")
fromConfig := c.Bool("from-config") fromConfig := c.Bool("from-config")
@ -93,6 +96,23 @@ func execSubscribe(c *cli.Context) error {
if since != "" { if since != "" {
options = append(options, client.WithSince(since)) options = append(options, client.WithSince(since))
} }
if user != "" {
var pass string
parts := strings.SplitN(user, ":", 2)
if len(parts) == 2 {
user = parts[0]
pass = parts[1]
} else {
fmt.Fprint(c.App.ErrWriter, "Enter Password: ")
p, err := util.ReadPassword(c.App.Reader)
if err != nil {
return err
}
pass = string(p)
fmt.Fprintf(c.App.ErrWriter, "\r%s\r", strings.Repeat(" ", 20))
}
options = append(options, client.WithBasicAuth(user, pass))
}
if poll { if poll {
options = append(options, client.WithPoll()) options = append(options, client.WithPoll())
} }
@ -142,6 +162,9 @@ func doSubscribe(c *cli.Context, cl *client.Client, conf *client.Config, topic,
for filter, value := range s.If { for filter, value := range s.If {
topicOptions = append(topicOptions, client.WithFilter(filter, value)) topicOptions = append(topicOptions, client.WithFilter(filter, value))
} }
if s.User != "" && s.Password != "" {
topicOptions = append(topicOptions, client.WithBasicAuth(s.User, s.Password))
}
subscriptionID := cl.Subscribe(s.Topic, topicOptions...) subscriptionID := cl.Subscribe(s.Topic, topicOptions...)
commands[subscriptionID] = s.Command commands[subscriptionID] = s.Command
} }

View File

@ -196,3 +196,27 @@ EOF
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl restart ntfy-client sudo systemctl restart ntfy-client
``` ```
### Authentication
Depending on whether the server is configured to support [access control](../config.md#access-control), some topics
may be read/write protected so that only users with the correct credentials can subscribe or publish to them.
To publish/subscribe to protected topics, you can use [Basic Auth](https://en.wikipedia.org/wiki/Basic_access_authentication)
with a valid username/password. For your self-hosted server, **be sure to use HTTPS to avoid eavesdropping** and exposing
your password.
You can either add your username and password to the configuration file:
=== "~/.config/ntfy/client.yml"
```yaml
- topic: secret
command: 'notify-send "$m"'
user: phill
password: mypass
```
Or with the `ntfy subscibe` command:
```
ntfy subscribe \
-u phil:mypass \
ntfy.example.com/mysecrets
```