Replace read/write flags with Permission

This commit is contained in:
binwiederhier 2023-01-02 21:12:42 -05:00
parent 1733323132
commit 4b9d40464c
13 changed files with 194 additions and 152 deletions

View file

@ -1,6 +1,7 @@
package server
import (
"heckel.io/ntfy/user"
"io/fs"
"net/netip"
"time"
@ -66,8 +67,7 @@ type Config struct {
CacheBatchSize int
CacheBatchTimeout time.Duration
AuthFile string
AuthDefaultRead bool
AuthDefaultWrite bool
AuthDefault user.Permission
AttachmentCacheDir string
AttachmentTotalSizeLimit int64
AttachmentFileSizeLimit int64
@ -127,8 +127,7 @@ func NewConfig() *Config {
CacheBatchSize: 0,
CacheBatchTimeout: 0,
AuthFile: "",
AuthDefaultRead: true,
AuthDefaultWrite: true,
AuthDefault: user.NewPermission(true, true),
AttachmentCacheDir: "",
AttachmentTotalSizeLimit: DefaultAttachmentTotalSizeLimit,
AttachmentFileSizeLimit: DefaultAttachmentFileSizeLimit,

View file

@ -41,8 +41,8 @@ var (
errHTTPBadRequestDelayTooLarge = &errHTTP{40006, http.StatusBadRequest, "invalid delay parameter: too large, please refer to the docs", "https://ntfy.sh/docs/publish/#scheduled-delivery"}
errHTTPBadRequestPriorityInvalid = &errHTTP{40007, http.StatusBadRequest, "invalid priority parameter", "https://ntfy.sh/docs/publish/#message-priority"}
errHTTPBadRequestSinceInvalid = &errHTTP{40008, http.StatusBadRequest, "invalid since parameter", "https://ntfy.sh/docs/subscribe/api/#fetch-cached-messages"}
errHTTPBadRequestTopicInvalid = &errHTTP{40009, http.StatusBadRequest, "invalid topic: topic invalid", ""}
errHTTPBadRequestTopicDisallowed = &errHTTP{40010, http.StatusBadRequest, "invalid topic: topic name is disallowed", ""}
errHTTPBadRequestTopicInvalid = &errHTTP{40009, http.StatusBadRequest, "invalid request: topic invalid", ""}
errHTTPBadRequestTopicDisallowed = &errHTTP{40010, http.StatusBadRequest, "invalid request: topic name is disallowed", ""}
errHTTPBadRequestMessageNotUTF8 = &errHTTP{40011, http.StatusBadRequest, "invalid message: message must be UTF-8 encoded", ""}
errHTTPBadRequestAttachmentURLInvalid = &errHTTP{40013, http.StatusBadRequest, "invalid request: attachment URL is invalid", "https://ntfy.sh/docs/publish/#attachments"}
errHTTPBadRequestAttachmentsDisallowed = &errHTTP{40014, http.StatusBadRequest, "invalid request: attachments not allowed", "https://ntfy.sh/docs/config/#attachments"}
@ -56,6 +56,7 @@ var (
errHTTPBadRequestSignupNotEnabled = &errHTTP{40022, http.StatusBadRequest, "invalid request: signup not enabled", "https://ntfy.sh/docs/config"}
errHTTPBadRequestNoTokenProvided = &errHTTP{40023, http.StatusBadRequest, "invalid request: no token provided", ""}
errHTTPBadRequestJSONInvalid = &errHTTP{40024, http.StatusBadRequest, "invalid request: request body must be valid JSON", ""}
errHTTPBadRequestPermissionInvalid = &errHTTP{40025, http.StatusBadRequest, "invalid request: incorrect permission string", ""}
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", ""}
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication"}
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication"}

View file

@ -165,7 +165,7 @@ func New(conf *Config) (*Server, error) {
}
var userManager *user.Manager
if conf.AuthFile != "" {
userManager, err = user.NewManager(conf.AuthFile, conf.AuthDefaultRead, conf.AuthDefaultWrite)
userManager, err = user.NewManager(conf.AuthFile, conf.AuthDefault)
if err != nil {
return nil, err
}

View file

@ -101,19 +101,9 @@ func (s *Server) handleAccountGet(w http.ResponseWriter, _ *http.Request, v *vis
if len(reservations) > 0 {
response.Reservations = make([]*apiAccountReservation, 0)
for _, r := range reservations {
var everyone string
if r.AllowEveryoneRead && r.AllowEveryoneWrite {
everyone = "read-write"
} else if r.AllowEveryoneRead && !r.AllowEveryoneWrite {
everyone = "read-only"
} else if !r.AllowEveryoneRead && r.AllowEveryoneWrite {
everyone = "write-only"
} else {
everyone = "deny-all"
}
response.Reservations = append(response.Reservations, &apiAccountReservation{
Topic: r.TopicPattern,
Everyone: everyone,
Topic: r.Topic,
Everyone: r.Everyone.String(),
})
}
}
@ -345,12 +335,14 @@ func (s *Server) handleAccountAccessAdd(w http.ResponseWriter, r *http.Request,
return errHTTPConflictTopicReserved
}
owner, username := v.user.Name, v.user.Name
everyoneRead := util.Contains([]string{"read-write", "rw", "read-only", "read", "ro"}, req.Everyone)
everyoneWrite := util.Contains([]string{"read-write", "rw", "write-only", "write", "wo"}, req.Everyone)
everyone, err := user.ParsePermission(req.Everyone)
if err != nil {
return errHTTPBadRequestPermissionInvalid
}
if err := s.userManager.AllowAccess(owner, username, req.Topic, true, true); err != nil {
return err
}
if err := s.userManager.AllowAccess(owner, user.Everyone, req.Topic, everyoneRead, everyoneWrite); err != nil {
if err := s.userManager.AllowAccess(owner, user.Everyone, req.Topic, everyone.IsRead(), everyone.IsWrite()); err != nil {
return err
}
w.Header().Set("Content-Type", "application/json")
@ -373,7 +365,7 @@ func (s *Server) handleAccountAccessDelete(w http.ResponseWriter, r *http.Reques
}
authorized := false
for _, r := range reservations {
if r.TopicPattern == topic {
if r.Topic == topic {
authorized = true
break
}

View file

@ -638,8 +638,7 @@ func TestServer_Auth_Success_Admin(t *testing.T) {
func TestServer_Auth_Success_User(t *testing.T) {
c := newTestConfig(t)
c.AuthFile = filepath.Join(t.TempDir(), "user.db")
c.AuthDefaultRead = false
c.AuthDefaultWrite = false
c.AuthDefault = user.PermissionDenyAll
s := newTestServer(t, c)
require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser))
@ -654,8 +653,7 @@ func TestServer_Auth_Success_User(t *testing.T) {
func TestServer_Auth_Success_User_MultipleTopics(t *testing.T) {
c := newTestConfig(t)
c.AuthFile = filepath.Join(t.TempDir(), "user.db")
c.AuthDefaultRead = false
c.AuthDefaultWrite = false
c.AuthDefault = user.PermissionDenyAll
s := newTestServer(t, c)
require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser))
@ -676,8 +674,7 @@ func TestServer_Auth_Success_User_MultipleTopics(t *testing.T) {
func TestServer_Auth_Fail_InvalidPass(t *testing.T) {
c := newTestConfig(t)
c.AuthFile = filepath.Join(t.TempDir(), "user.db")
c.AuthDefaultRead = false
c.AuthDefaultWrite = false
c.AuthDefault = user.PermissionDenyAll
s := newTestServer(t, c)
require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin))
@ -691,8 +688,7 @@ func TestServer_Auth_Fail_InvalidPass(t *testing.T) {
func TestServer_Auth_Fail_Unauthorized(t *testing.T) {
c := newTestConfig(t)
c.AuthFile = filepath.Join(t.TempDir(), "user.db")
c.AuthDefaultRead = false
c.AuthDefaultWrite = false
c.AuthDefault = user.PermissionDenyAll
s := newTestServer(t, c)
require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser))
@ -707,8 +703,7 @@ func TestServer_Auth_Fail_Unauthorized(t *testing.T) {
func TestServer_Auth_Fail_CannotPublish(t *testing.T) {
c := newTestConfig(t)
c.AuthFile = filepath.Join(t.TempDir(), "user.db")
c.AuthDefaultRead = true // Open by default
c.AuthDefaultWrite = true // Open by default
c.AuthDefault = user.PermissionReadWrite // Open by default
s := newTestServer(t, c)
require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin))
@ -739,8 +734,7 @@ func TestServer_Auth_Fail_CannotPublish(t *testing.T) {
func TestServer_Auth_ViaQuery(t *testing.T) {
c := newTestConfig(t)
c.AuthFile = filepath.Join(t.TempDir(), "user.db")
c.AuthDefaultRead = false
c.AuthDefaultWrite = false
c.AuthDefault = user.PermissionDenyAll
s := newTestServer(t, c)
require.Nil(t, s.userManager.AddUser("ben", "some pass", user.RoleAdmin))