More docs docs docs

This commit is contained in:
Philipp Heckel 2022-02-01 23:39:57 -05:00
parent c3a2331b59
commit 1552d8103e
7 changed files with 211 additions and 121 deletions

View file

@ -8,12 +8,6 @@ import (
"heckel.io/ntfy/util"
)
/*
*/
const (
userEveryone = "everyone"
)
@ -46,7 +40,8 @@ Usage:
ntfy access USERNAME TOPIC PERMISSION # Allow/deny access for USERNAME to TOPIC
Arguments:
USERNAME an existing user, as created with 'ntfy user add'
USERNAME an existing user, as created with 'ntfy user add', or "everyone"/"*"
to define access rules for anonymous/unauthenticated clients
TOPIC name of a topic with optional wildcards, e.g. "mytopic*"
PERMISSION one of the following:
- read-write (alias: rw)

View file

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/client"
"heckel.io/ntfy/util"
"io"
"os"
"path/filepath"
@ -15,23 +16,25 @@ var cmdPublish = &cli.Command{
Name: "publish",
Aliases: []string{"pub", "send", "trigger"},
Usage: "Send message via a ntfy server",
UsageText: "ntfy send [OPTIONS..] TOPIC [MESSAGE]",
UsageText: "ntfy send [OPTIONS..] TOPIC [MESSAGE]\n NTFY_TOPIC=.. ntfy send [OPTIONS..] -P [MESSAGE]",
Action: execPublish,
Category: categoryClient,
Flags: []cli.Flag{
&cli.StringFlag{Name: "config", Aliases: []string{"c"}, Usage: "client config file"},
&cli.StringFlag{Name: "title", Aliases: []string{"t"}, Usage: "message title"},
&cli.StringFlag{Name: "priority", Aliases: []string{"p"}, Usage: "priority of the message (1=min, 2=low, 3=default, 4=high, 5=max)"},
&cli.StringFlag{Name: "tags", Aliases: []string{"tag", "T"}, Usage: "comma separated list of tags and emojis"},
&cli.StringFlag{Name: "delay", Aliases: []string{"at", "in", "D"}, Usage: "delay/schedule message"},
&cli.StringFlag{Name: "click", Aliases: []string{"U"}, Usage: "URL to open when notification is clicked"},
&cli.StringFlag{Name: "attach", Aliases: []string{"a"}, Usage: "URL to send as an external attachment"},
&cli.StringFlag{Name: "filename", Aliases: []string{"name", "n"}, Usage: "Filename for the attachment"},
&cli.StringFlag{Name: "file", Aliases: []string{"f"}, Usage: "File to upload as an attachment"},
&cli.StringFlag{Name: "email", Aliases: []string{"e-mail", "mail", "e"}, Usage: "also send to e-mail address"},
&cli.BoolFlag{Name: "no-cache", Aliases: []string{"C"}, Usage: "do not cache message server-side"},
&cli.BoolFlag{Name: "no-firebase", Aliases: []string{"F"}, Usage: "do not forward message to Firebase"},
&cli.BoolFlag{Name: "quiet", Aliases: []string{"q"}, Usage: "do print message"},
&cli.StringFlag{Name: "config", Aliases: []string{"c"}, EnvVars: []string{"NTFY_CONFIG"}, Usage: "client config file"},
&cli.StringFlag{Name: "title", Aliases: []string{"t"}, EnvVars: []string{"NTFY_TITLE"}, Usage: "message title"},
&cli.StringFlag{Name: "priority", Aliases: []string{"p"}, EnvVars: []string{"NTFY_PRIORITY"}, Usage: "priority of the message (1=min, 2=low, 3=default, 4=high, 5=max)"},
&cli.StringFlag{Name: "tags", Aliases: []string{"tag", "T"}, EnvVars: []string{"NTFY_TAGS"}, Usage: "comma separated list of tags and emojis"},
&cli.StringFlag{Name: "delay", Aliases: []string{"at", "in", "D"}, EnvVars: []string{"NTFY_DELAY"}, Usage: "delay/schedule message"},
&cli.StringFlag{Name: "click", Aliases: []string{"U"}, EnvVars: []string{"NTFY_CLICK"}, Usage: "URL to open when notification is clicked"},
&cli.StringFlag{Name: "attach", Aliases: []string{"a"}, EnvVars: []string{"NTFY_ATTACH"}, Usage: "URL to send as an external attachment"},
&cli.StringFlag{Name: "filename", Aliases: []string{"name", "n"}, EnvVars: []string{"NTFY_FILENAME"}, Usage: "Filename for the attachment"},
&cli.StringFlag{Name: "file", Aliases: []string{"f"}, EnvVars: []string{"NTFY_FILE"}, Usage: "File to upload as an attachment"},
&cli.StringFlag{Name: "email", Aliases: []string{"mail", "e"}, EnvVars: []string{"NTFY_EMAIL"}, Usage: "also send to e-mail address"},
&cli.StringFlag{Name: "user", Aliases: []string{"u"}, EnvVars: []string{"NTFY_USER"}, Usage: "username[:password] used to auth against the server"},
&cli.BoolFlag{Name: "no-cache", Aliases: []string{"C"}, EnvVars: []string{"NTFY_NO_CACHE"}, Usage: "do not cache message server-side"},
&cli.BoolFlag{Name: "no-firebase", Aliases: []string{"F"}, EnvVars: []string{"NTFY_NO_FIREBASE"}, Usage: "do not forward message to Firebase"},
&cli.BoolFlag{Name: "env-topic", Aliases: []string{"P"}, EnvVars: []string{"NTFY_ENV_TOPIC"}, Usage: "use topic from NTFY_TOPIC env variable"},
&cli.BoolFlag{Name: "quiet", Aliases: []string{"q"}, EnvVars: []string{"NTFY_QUIET"}, Usage: "do print message"},
},
Description: `Publish a message to a ntfy server.
@ -46,9 +49,12 @@ Examples:
ntfy pub --click="https://reddit.com" redd 'New msg' # Opens Reddit when notification is clicked
ntfy pub --attach="http://some.tld/file.zip" files # Send ZIP archive from URL as attachment
ntfy pub --file=flower.jpg flowers 'Nice!' # Send image.jpg as attachment
ntfy pub -u phil:mypass secret Psst # Publish with username/password
NTFY_USER=phil:mypass ntfy pub secret Psst # Use env variables to set username/password
NTFY_TOPIC=mytopic ntfy pub -P "some message"" # Use NTFY_TOPIC variable as topic
cat flower.jpg | ntfy pub --file=- flowers 'Nice!' # Same as above, send image.jpg as attachment
ntfy trigger mywebhook # Sending without message, useful for webhooks
Please also check out the docs on publishing messages. Especially for the --tags and --delay options,
it has incredibly useful information: https://ntfy.sh/docs/publish/.
@ -57,9 +63,6 @@ or ~/.config/ntfy/client.yml for all other users.`,
}
func execPublish(c *cli.Context) error {
if c.NArg() < 1 {
return errors.New("must specify topic, type 'ntfy publish --help' for help")
}
conf, err := loadConfig(c)
if err != nil {
return err
@ -73,13 +76,25 @@ func execPublish(c *cli.Context) error {
filename := c.String("filename")
file := c.String("file")
email := c.String("email")
user := c.String("user")
noCache := c.Bool("no-cache")
noFirebase := c.Bool("no-firebase")
envTopic := c.Bool("env-topic")
quiet := c.Bool("quiet")
topic := c.Args().Get(0)
message := ""
if c.NArg() > 1 {
message = strings.Join(c.Args().Slice()[1:], " ")
var topic, message string
if envTopic {
topic = os.Getenv("NTFY_TOPIC")
if c.NArg() > 0 {
message = strings.Join(c.Args().Slice(), " ")
}
} else {
if c.NArg() < 1 {
return errors.New("must specify topic, type 'ntfy publish --help' for help")
}
topic = c.Args().Get(0)
if c.NArg() > 1 {
message = strings.Join(c.Args().Slice()[1:], " ")
}
}
var options []client.PublishOption
if title != "" {
@ -112,6 +127,23 @@ func execPublish(c *cli.Context) error {
if noFirebase {
options = append(options, client.WithNoFirebase())
}
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))
}
var body io.Reader
if file == "" {
body = strings.NewReader(message)

View file

@ -11,67 +11,108 @@ import (
"strings"
)
/*
---
dabbling for CLI
ntfy user allow phil mytopic
ntfy user allow phil mytopic --read-only
ntfy user deny phil mytopic
ntfy user list
phil (admin)
- read-write access to everything
ben (user)
- read-write access to a topic alerts
- read access to
everyone (no user)
- read-only access to topic announcements
*/
var flagsUser = userCommandFlags()
var cmdUser = &cli.Command{
Name: "user",
Usage: "Manage users and access to topics",
UsageText: "ntfy user [add|del|...] ...",
Usage: "Manage/show users",
UsageText: "ntfy user [list|add|remove|change-pass|change-role] ...",
Flags: flagsUser,
Before: initConfigFileInputSource("config", flagsUser),
Category: categoryServer,
Subcommands: []*cli.Command{
{
Name: "add",
Aliases: []string{"a"},
Usage: "add user to auth database",
Action: execUserAdd,
Name: "add",
Aliases: []string{"a"},
Usage: "add user",
UsageText: "ntfy user add [--role=admin|user] USERNAME",
Action: execUserAdd,
Flags: []cli.Flag{
&cli.StringFlag{Name: "role", Aliases: []string{"r"}, Value: string(auth.RoleUser), Usage: "user role"},
},
Description: `Add a new user to the ntfy user database.
A user can be either a regular user, or an admin. A regular user has no read or write access (unless
granted otherwise by the auth-default-access setting). An admin user has read and write access to all
topics.
Examples:
ntfy user add phil # Add regular user phil
ntfy user add --role=admin phil # Add admin user phil
`,
},
{
Name: "remove",
Aliases: []string{"del", "rm"},
Usage: "remove user from auth database",
Action: execUserDel,
Name: "remove",
Aliases: []string{"del", "rm"},
Usage: "remove user",
UsageText: "ntfy user remove USERNAME",
Action: execUserDel,
Description: `Remove a user from the ntfy user database.
Example:
ntfy user del phil
`,
},
{
Name: "change-pass",
Aliases: []string{"chp"},
Usage: "change user password",
Action: execUserChangePass,
Name: "change-pass",
Aliases: []string{"chp"},
Usage: "change user password",
UsageText: "ntfy user change-pass USERNAME",
Action: execUserChangePass,
Description: `Change the password for the given user.
The new password will be read from STDIN, and it'll be confirmed by typing
it twice.
Example:
ntfy user change-pass phil
`,
},
{
Name: "change-role",
Aliases: []string{"chr"},
Usage: "change user role",
Action: execUserChangeRole,
Name: "change-role",
Aliases: []string{"chr"},
Usage: "change user role",
UsageText: "ntfy user change-role USERNAME ROLE",
Action: execUserChangeRole,
Description: `Change the role for the given user to admin or user.
This command can be used to change the role of a user either from a regular user
to an admin user, or the other way around:
- admin: an admin has read/write access to all topics
- user: a regular user only has access to what was explicitly granted via 'ntfy access'
When changing the role of a user to "admin", all access control entries for that
user are removed, since they are no longer necessary.
Example:
ntfy user change-role phil admin # Make user phil an admin
ntfy user change-role phil user # Remove admin role from user phil
`,
},
{
Name: "list",
Aliases: []string{"chr"},
Usage: "change user role",
Aliases: []string{"l"},
Usage: "list users",
Action: execUserList,
},
},
Description: `Manage users of the ntfy server.
This is a server-only command. It directly manages the user.db as defined in the server config
file server.yml. The command only works if 'auth-file' is properly defined. Please also refer
to the related command 'ntfy access'.
The command allows you to add/remove/change users in the ntfy user database, as well as change
passwords or roles.
Examples:
ntfy user list # Shows list of users
ntfy user add phil # Add regular user phil
ntfy user add --role=admin phil # Add admin user phil
ntfy user del phil # Delete user phil
ntfy user change-pass phil # Change password for user phil
ntfy user change-role phil admin # Make user phil an admin
`,
}
func execUserAdd(c *cli.Context) error {
@ -79,6 +120,8 @@ func execUserAdd(c *cli.Context) error {
role := auth.Role(c.String("role"))
if username == "" {
return errors.New("username expected, type 'ntfy user add --help' for help")
} else if username == userEveryone {
return errors.New("username not allowed")
} else if !auth.AllowedRole(role) {
return errors.New("role must be either 'user' or 'admin'")
}
@ -101,6 +144,8 @@ func execUserDel(c *cli.Context) error {
username := c.Args().Get(0)
if username == "" {
return errors.New("username expected, type 'ntfy user del --help' for help")
} else if username == userEveryone {
return errors.New("username not allowed")
}
manager, err := createAuthManager(c)
if err != nil {
@ -117,6 +162,8 @@ func execUserChangePass(c *cli.Context) error {
username := c.Args().Get(0)
if username == "" {
return errors.New("username expected, type 'ntfy user change-pass --help' for help")
} else if username == userEveryone {
return errors.New("username not allowed")
}
password, err := readPassword(c)
if err != nil {
@ -138,6 +185,8 @@ func execUserChangeRole(c *cli.Context) error {
role := auth.Role(c.Args().Get(1))
if username == "" || !auth.AllowedRole(role) {
return errors.New("username and new role expected, type 'ntfy user change-role --help' for help")
} else if username == userEveryone {
return errors.New("username not allowed")
}
manager, err := createAuthManager(c)
if err != nil {
@ -169,11 +218,11 @@ func createAuthManager(c *cli.Context) (auth.Manager, error) {
return nil, errors.New("option auth-file not set; auth is unconfigured for this server")
} else if !util.FileExists(authFile) {
return nil, errors.New("auth-file does not exist; please start the server at least once to create it")
} else if !util.InStringList([]string{"read-write", "read-only", "deny-all"}, authDefaultAccess) {
} else if !util.InStringList([]string{"read-write", "read-only", "write-only", "deny-all"}, authDefaultAccess) {
return nil, errors.New("if set, auth-default-access must start set to 'read-write', 'read-only' or 'deny-all'")
}
authDefaultRead := authDefaultAccess == "read-write" || authDefaultAccess == "read-only"
authDefaultWrite := authDefaultAccess == "read-write"
authDefaultWrite := authDefaultAccess == "read-write" || authDefaultAccess == "write-only"
return auth.NewSQLiteAuth(authFile, authDefaultRead, authDefaultWrite)
}