access.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package cmd
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/urfave/cli/v2"
  6. "heckel.io/ntfy/auth"
  7. "heckel.io/ntfy/util"
  8. )
  9. /*
  10. */
  11. const (
  12. userEveryone = "everyone"
  13. )
  14. var flagsAccess = append(
  15. userCommandFlags(),
  16. &cli.BoolFlag{Name: "reset", Aliases: []string{"r"}, Usage: "reset access for user (and topic)"},
  17. )
  18. var cmdAccess = &cli.Command{
  19. Name: "access",
  20. Usage: "Grant/revoke access to a topic, or show access",
  21. UsageText: "ntfy access [USERNAME [TOPIC [PERMISSION]]]",
  22. Flags: flagsAccess,
  23. Before: initConfigFileInputSource("config", flagsAccess),
  24. Action: execUserAccess,
  25. Category: categoryServer,
  26. Description: `Manage the access control list for the ntfy server.
  27. This is a server-only command. It directly manages the user.db as defined in the server config
  28. file server.yml. The command only works if 'auth-file' is properly defined. Please also refer
  29. to the related command 'ntfy user'.
  30. The command allows you to show the access control list, as well as change it, depending on how
  31. it is called.
  32. Usage:
  33. ntfy access # Shows the entire access control list
  34. ntfy access USERNAME # Shows access control entries for USERNAME
  35. ntfy access USERNAME TOPIC PERMISSION # Allow/deny access for USERNAME to TOPIC
  36. Arguments:
  37. USERNAME an existing user, as created with 'ntfy user add'
  38. TOPIC name of a topic with optional wildcards, e.g. "mytopic*"
  39. PERMISSION one of the following:
  40. - read-write (alias: rw)
  41. - read-only (aliases: read, ro)
  42. - write-only (aliases: write, wo)
  43. - deny (alias: none)
  44. Examples:
  45. ntfy access
  46. ntfy access phil # Shows access for user phil
  47. ntfy access phil mytopic rw # Allow read-write access to mytopic for user phil
  48. ntfy access everyone mytopic rw # Allow anonymous read-write access to mytopic
  49. ntfy access everyone "up*" write # Allow anonymous write-only access to topics "up..."
  50. ntfy access --reset # Reset entire access control list
  51. ntfy access --reset phil # Reset all access for user phil
  52. ntfy access --reset phil mytopic # Reset access for user phil and topic mytopic
  53. `,
  54. }
  55. func execUserAccess(c *cli.Context) error {
  56. if c.NArg() > 3 {
  57. return errors.New("too many arguments, please check 'ntfy access --help' for usage details")
  58. }
  59. manager, err := createAuthManager(c)
  60. if err != nil {
  61. return err
  62. }
  63. username := c.Args().Get(0)
  64. if username == userEveryone {
  65. username = auth.Everyone
  66. }
  67. topic := c.Args().Get(1)
  68. perms := c.Args().Get(2)
  69. reset := c.Bool("reset")
  70. if reset {
  71. if perms != "" {
  72. return errors.New("too many arguments, please check 'ntfy access --help' for usage details")
  73. }
  74. return resetAccess(c, manager, username, topic)
  75. } else if perms == "" {
  76. return showAccess(c, manager, username)
  77. }
  78. return changeAccess(c, manager, username, topic, perms)
  79. }
  80. func changeAccess(c *cli.Context, manager auth.Manager, username string, topic string, perms string) error {
  81. if !util.InStringList([]string{"", "read-write", "rw", "read-only", "read", "ro", "write-only", "write", "wo", "none", "deny"}, perms) {
  82. return errors.New("permission must be one of: read-write, read-only, write-only, or deny (or the aliases: read, ro, write, wo, none)")
  83. }
  84. read := util.InStringList([]string{"read-write", "rw", "read-only", "read", "ro"}, perms)
  85. write := util.InStringList([]string{"read-write", "rw", "write-only", "write", "wo"}, perms)
  86. if err := manager.AllowAccess(username, topic, read, write); err != nil {
  87. return err
  88. }
  89. if read && write {
  90. fmt.Fprintf(c.App.Writer, "Granted read-write access to topic %s\n\n", topic)
  91. } else if read {
  92. fmt.Fprintf(c.App.Writer, "Granted read-only access to topic %s\n\n", topic)
  93. } else if write {
  94. fmt.Fprintf(c.App.Writer, "Granted write-only access to topic %s\n\n", topic)
  95. } else {
  96. fmt.Fprintf(c.App.Writer, "Revoked all access to topic %s\n\n", topic)
  97. }
  98. return showUserAccess(c, manager, username)
  99. }
  100. func resetAccess(c *cli.Context, manager auth.Manager, username, topic string) error {
  101. if username == "" {
  102. return resetAllAccess(c, manager)
  103. } else if topic == "" {
  104. return resetUserAccess(c, manager, username)
  105. }
  106. return resetUserTopicAccess(c, manager, username, topic)
  107. }
  108. func resetAllAccess(c *cli.Context, manager auth.Manager) error {
  109. if err := manager.ResetAccess("", ""); err != nil {
  110. return err
  111. }
  112. fmt.Fprintln(c.App.Writer, "Reset access for all users")
  113. return nil
  114. }
  115. func resetUserAccess(c *cli.Context, manager auth.Manager, username string) error {
  116. if err := manager.ResetAccess(username, ""); err != nil {
  117. return err
  118. }
  119. fmt.Fprintf(c.App.Writer, "Reset access for user %s\n\n", username)
  120. return showUserAccess(c, manager, username)
  121. }
  122. func resetUserTopicAccess(c *cli.Context, manager auth.Manager, username string, topic string) error {
  123. if err := manager.ResetAccess(username, topic); err != nil {
  124. return err
  125. }
  126. fmt.Fprintf(c.App.Writer, "Reset access for user %s and topic %s\n\n", username, topic)
  127. return showUserAccess(c, manager, username)
  128. }
  129. func showAccess(c *cli.Context, manager auth.Manager, username string) error {
  130. if username == "" {
  131. return showAllAccess(c, manager)
  132. }
  133. return showUserAccess(c, manager, username)
  134. }
  135. func showAllAccess(c *cli.Context, manager auth.Manager) error {
  136. users, err := manager.Users()
  137. if err != nil {
  138. return err
  139. }
  140. return showUsers(c, manager, users)
  141. }
  142. func showUserAccess(c *cli.Context, manager auth.Manager, username string) error {
  143. users, err := manager.User(username)
  144. if err != nil {
  145. return err
  146. }
  147. return showUsers(c, manager, []*auth.User{users})
  148. }
  149. func showUsers(c *cli.Context, manager auth.Manager, users []*auth.User) error {
  150. for _, user := range users {
  151. fmt.Fprintf(c.App.Writer, "User %s (%s)\n", user.Name, user.Role)
  152. if user.Role == auth.RoleAdmin {
  153. fmt.Fprintf(c.App.ErrWriter, "- read-write access to all topics (admin role)\n")
  154. } else if len(user.Grants) > 0 {
  155. for _, grant := range user.Grants {
  156. if grant.AllowRead && grant.AllowWrite {
  157. fmt.Fprintf(c.App.ErrWriter, "- read-write access to topic %s\n", grant.TopicPattern)
  158. } else if grant.AllowRead {
  159. fmt.Fprintf(c.App.ErrWriter, "- read-only access to topic %s\n", grant.TopicPattern)
  160. } else if grant.AllowWrite {
  161. fmt.Fprintf(c.App.ErrWriter, "- write-only access to topic %s\n", grant.TopicPattern)
  162. } else {
  163. fmt.Fprintf(c.App.ErrWriter, "- no access to topic %s\n", grant.TopicPattern)
  164. }
  165. }
  166. } else {
  167. fmt.Fprintf(c.App.ErrWriter, "- no topic-specific permissions\n")
  168. }
  169. if user.Name == auth.Everyone {
  170. defaultRead, defaultWrite := manager.DefaultAccess()
  171. if defaultRead && defaultWrite {
  172. fmt.Fprintln(c.App.ErrWriter, "- read-write access to all (other) topics (server config)")
  173. } else if defaultRead {
  174. fmt.Fprintln(c.App.ErrWriter, "- read-only access to all (other) topics (server config)")
  175. } else if defaultWrite {
  176. fmt.Fprintln(c.App.ErrWriter, "- write-only access to all (other) topics (server config)")
  177. } else {
  178. fmt.Fprintln(c.App.ErrWriter, "- no access to any (other) topics (server config)")
  179. }
  180. }
  181. }
  182. return nil
  183. }