Merge pull request #148 from lrabane/cli-auth

Add authentification support for subscribing with CLI
pull/150/head
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"'
# if:
# priority: high,urgent
# - topic: secret
# command: 'notify-send "$m"'
# user: phill
# password: mypass
#
# Variables:
# Variable Aliases Description

View File

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

View File

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

View File

@ -23,6 +23,7 @@ var cmdSubscribe = &cli.Command{
Flags: []cli.Flag{
&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: "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: "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"},
@ -40,6 +41,7 @@ ntfy subscribe TOPIC
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 --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
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)
since := c.String("since")
user := c.String("user")
poll := c.Bool("poll")
scheduled := c.Bool("scheduled")
fromConfig := c.Bool("from-config")
@ -93,6 +96,23 @@ func execSubscribe(c *cli.Context) error {
if 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 {
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 {
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...)
commands[subscriptionID] = s.Command
}

View File

@ -196,3 +196,27 @@ EOF
sudo systemctl daemon-reload
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
```