Added disallowed-topics
parent
b37cf02a6e
commit
bcb22d8d4c
|
@ -58,6 +58,7 @@ var flagsServe = append(
|
||||||
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "attachment-expiry-duration", Aliases: []string{"attachment_expiry_duration", "X"}, EnvVars: []string{"NTFY_ATTACHMENT_EXPIRY_DURATION"}, Value: server.DefaultAttachmentExpiryDuration, DefaultText: "3h", Usage: "duration after which uploaded attachments will be deleted (e.g. 3h, 20h)"}),
|
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "attachment-expiry-duration", Aliases: []string{"attachment_expiry_duration", "X"}, EnvVars: []string{"NTFY_ATTACHMENT_EXPIRY_DURATION"}, Value: server.DefaultAttachmentExpiryDuration, DefaultText: "3h", Usage: "duration after which uploaded attachments will be deleted (e.g. 3h, 20h)"}),
|
||||||
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "keepalive-interval", Aliases: []string{"keepalive_interval", "k"}, EnvVars: []string{"NTFY_KEEPALIVE_INTERVAL"}, Value: server.DefaultKeepaliveInterval, Usage: "interval of keepalive messages"}),
|
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "keepalive-interval", Aliases: []string{"keepalive_interval", "k"}, EnvVars: []string{"NTFY_KEEPALIVE_INTERVAL"}, Value: server.DefaultKeepaliveInterval, Usage: "interval of keepalive messages"}),
|
||||||
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "manager-interval", Aliases: []string{"manager_interval", "m"}, EnvVars: []string{"NTFY_MANAGER_INTERVAL"}, Value: server.DefaultManagerInterval, Usage: "interval of for message pruning and stats printing"}),
|
altsrc.NewDurationFlag(&cli.DurationFlag{Name: "manager-interval", Aliases: []string{"manager_interval", "m"}, EnvVars: []string{"NTFY_MANAGER_INTERVAL"}, Value: server.DefaultManagerInterval, Usage: "interval of for message pruning and stats printing"}),
|
||||||
|
altsrc.NewStringSliceFlag(&cli.StringSliceFlag{Name: "disallowed-topics", Aliases: []string{"disallowed_topics"}, EnvVars: []string{"NTFY_DISALLOWED_TOPICS"}, Usage: "topics that are not allowed to be used"}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "web-root", Aliases: []string{"web_root"}, EnvVars: []string{"NTFY_WEB_ROOT"}, Value: "app", Usage: "sets web root to landing page (home), web app (app) or disabled (disable)"}),
|
altsrc.NewStringFlag(&cli.StringFlag{Name: "web-root", Aliases: []string{"web_root"}, EnvVars: []string{"NTFY_WEB_ROOT"}, Value: "app", Usage: "sets web root to landing page (home), web app (app) or disabled (disable)"}),
|
||||||
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-signup", Aliases: []string{"enable_signup"}, EnvVars: []string{"NTFY_ENABLE_SIGNUP"}, Value: false, Usage: "allows users to sign up via the web app, or API"}),
|
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-signup", Aliases: []string{"enable_signup"}, EnvVars: []string{"NTFY_ENABLE_SIGNUP"}, Value: false, Usage: "allows users to sign up via the web app, or API"}),
|
||||||
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-login", Aliases: []string{"enable_login"}, EnvVars: []string{"NTFY_ENABLE_LOGIN"}, Value: false, Usage: "allows users to log in via the web app, or API"}),
|
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "enable-login", Aliases: []string{"enable_login"}, EnvVars: []string{"NTFY_ENABLE_LOGIN"}, Value: false, Usage: "allows users to log in via the web app, or API"}),
|
||||||
|
@ -132,6 +133,7 @@ func execServe(c *cli.Context) error {
|
||||||
attachmentExpiryDuration := c.Duration("attachment-expiry-duration")
|
attachmentExpiryDuration := c.Duration("attachment-expiry-duration")
|
||||||
keepaliveInterval := c.Duration("keepalive-interval")
|
keepaliveInterval := c.Duration("keepalive-interval")
|
||||||
managerInterval := c.Duration("manager-interval")
|
managerInterval := c.Duration("manager-interval")
|
||||||
|
disallowedTopics := c.StringSlice("disallowed-topics")
|
||||||
webRoot := c.String("web-root")
|
webRoot := c.String("web-root")
|
||||||
enableSignup := c.Bool("enable-signup")
|
enableSignup := c.Bool("enable-signup")
|
||||||
enableLogin := c.Bool("enable-login")
|
enableLogin := c.Bool("enable-login")
|
||||||
|
@ -251,6 +253,9 @@ func execServe(c *cli.Context) error {
|
||||||
stripe.Key = stripeSecretKey
|
stripe.Key = stripeSecretKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add default forbidden topics
|
||||||
|
disallowedTopics = append(disallowedTopics, server.DefaultDisallowedTopics...)
|
||||||
|
|
||||||
// Run server
|
// Run server
|
||||||
conf := server.NewConfig()
|
conf := server.NewConfig()
|
||||||
conf.File = config
|
conf.File = config
|
||||||
|
@ -276,6 +281,7 @@ func execServe(c *cli.Context) error {
|
||||||
conf.AttachmentExpiryDuration = attachmentExpiryDuration
|
conf.AttachmentExpiryDuration = attachmentExpiryDuration
|
||||||
conf.KeepaliveInterval = keepaliveInterval
|
conf.KeepaliveInterval = keepaliveInterval
|
||||||
conf.ManagerInterval = managerInterval
|
conf.ManagerInterval = managerInterval
|
||||||
|
conf.DisallowedTopics = disallowedTopics
|
||||||
conf.WebRootIsApp = webRootIsApp
|
conf.WebRootIsApp = webRootIsApp
|
||||||
conf.UpstreamBaseURL = upstreamBaseURL
|
conf.UpstreamBaseURL = upstreamBaseURL
|
||||||
conf.SMTPSenderAddr = smtpSenderAddr
|
conf.SMTPSenderAddr = smtpSenderAddr
|
||||||
|
|
|
@ -58,6 +58,10 @@ const (
|
||||||
var (
|
var (
|
||||||
// DefaultVisitorStatsResetTime defines the time at which visitor stats are reset (wall clock only)
|
// DefaultVisitorStatsResetTime defines the time at which visitor stats are reset (wall clock only)
|
||||||
DefaultVisitorStatsResetTime = time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC)
|
DefaultVisitorStatsResetTime = time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
// DefaultDisallowedTopics defines the topics that are forbidden, because they are used elsewhere. This array can be
|
||||||
|
// extended using the server.yml config. If updated, also update in Android and web app.
|
||||||
|
DefaultDisallowedTopics = []string{"docs", "static", "file", "app", "account", "settings", "signup", "login"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is the main config struct for the application. Use New to instantiate a default config struct.
|
// Config is the main config struct for the application. Use New to instantiate a default config struct.
|
||||||
|
@ -87,6 +91,7 @@ type Config struct {
|
||||||
AttachmentExpiryDuration time.Duration
|
AttachmentExpiryDuration time.Duration
|
||||||
KeepaliveInterval time.Duration
|
KeepaliveInterval time.Duration
|
||||||
ManagerInterval time.Duration
|
ManagerInterval time.Duration
|
||||||
|
DisallowedTopics []string
|
||||||
WebRootIsApp bool
|
WebRootIsApp bool
|
||||||
DelayedSenderInterval time.Duration
|
DelayedSenderInterval time.Duration
|
||||||
FirebaseKeepaliveInterval time.Duration
|
FirebaseKeepaliveInterval time.Duration
|
||||||
|
|
|
@ -52,7 +52,7 @@ var (
|
||||||
errHTTPBadRequestPriorityInvalid = &errHTTP{40007, http.StatusBadRequest, "invalid priority parameter", "https://ntfy.sh/docs/publish/#message-priority"}
|
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"}
|
errHTTPBadRequestSinceInvalid = &errHTTP{40008, http.StatusBadRequest, "invalid since parameter", "https://ntfy.sh/docs/subscribe/api/#fetch-cached-messages"}
|
||||||
errHTTPBadRequestTopicInvalid = &errHTTP{40009, http.StatusBadRequest, "invalid request: topic invalid", ""}
|
errHTTPBadRequestTopicInvalid = &errHTTP{40009, http.StatusBadRequest, "invalid request: topic invalid", ""}
|
||||||
errHTTPBadRequestTopicDisallowed = &errHTTP{40010, http.StatusBadRequest, "invalid request: topic name is disallowed", ""}
|
errHTTPBadRequestTopicDisallowed = &errHTTP{40010, http.StatusBadRequest, "invalid request: topic name is not allowed", ""}
|
||||||
errHTTPBadRequestMessageNotUTF8 = &errHTTP{40011, http.StatusBadRequest, "invalid message: message must be UTF-8 encoded", ""}
|
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"}
|
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"}
|
errHTTPBadRequestAttachmentsDisallowed = &errHTTP{40014, http.StatusBadRequest, "invalid request: attachments not allowed", "https://ntfy.sh/docs/config/#attachments"}
|
||||||
|
|
|
@ -39,7 +39,8 @@ import (
|
||||||
- api
|
- api
|
||||||
- HIGH Self-review
|
- HIGH Self-review
|
||||||
- MEDIUM: Test for expiring messages after reservation removal
|
- MEDIUM: Test for expiring messages after reservation removal
|
||||||
- MEDIUM: disallowed-topics
|
- MEDIUM: uploading attachments leads to 404 -- race
|
||||||
|
- MEDIUM: Do not call tiers endoint when payments is not enabled
|
||||||
- MEDIUM: Test new token endpoints & never-expiring token
|
- MEDIUM: Test new token endpoints & never-expiring token
|
||||||
- LOW: UI: Flickering upgrade banner when logging in
|
- LOW: UI: Flickering upgrade banner when logging in
|
||||||
|
|
||||||
|
@ -103,7 +104,6 @@ var (
|
||||||
staticRegex = regexp.MustCompile(`^/static/.+`)
|
staticRegex = regexp.MustCompile(`^/static/.+`)
|
||||||
docsRegex = regexp.MustCompile(`^/docs(|/.*)$`)
|
docsRegex = regexp.MustCompile(`^/docs(|/.*)$`)
|
||||||
fileRegex = regexp.MustCompile(`^/file/([-_A-Za-z0-9]{1,64})(?:\.[A-Za-z0-9]{1,16})?$`)
|
fileRegex = regexp.MustCompile(`^/file/([-_A-Za-z0-9]{1,64})(?:\.[A-Za-z0-9]{1,16})?$`)
|
||||||
disallowedTopics = []string{"docs", "static", "file", "app", "account", "settings", "pricing", "signup", "login", "reset-password"} // If updated, also update in Android and web app
|
|
||||||
urlRegex = regexp.MustCompile(`^https?://`)
|
urlRegex = regexp.MustCompile(`^https?://`)
|
||||||
|
|
||||||
//go:embed site
|
//go:embed site
|
||||||
|
@ -496,7 +496,7 @@ func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visi
|
||||||
EnableSignup: s.config.EnableSignup,
|
EnableSignup: s.config.EnableSignup,
|
||||||
EnablePayments: s.config.StripeSecretKey != "",
|
EnablePayments: s.config.StripeSecretKey != "",
|
||||||
EnableReservations: s.config.EnableReservations,
|
EnableReservations: s.config.EnableReservations,
|
||||||
DisallowedTopics: disallowedTopics,
|
DisallowedTopics: s.config.DisallowedTopics,
|
||||||
}
|
}
|
||||||
b, err := json.MarshalIndent(response, "", " ")
|
b, err := json.MarshalIndent(response, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1260,7 +1260,7 @@ func (s *Server) topicsFromIDs(ids ...string) ([]*topic, error) {
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
topics := make([]*topic, 0)
|
topics := make([]*topic, 0)
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
if util.Contains(disallowedTopics, id) {
|
if util.Contains(s.config.DisallowedTopics, id) {
|
||||||
return nil, errHTTPBadRequestTopicDisallowed
|
return nil, errHTTPBadRequestTopicDisallowed
|
||||||
}
|
}
|
||||||
if _, ok := s.topics[id]; !ok {
|
if _, ok := s.topics[id]; !ok {
|
||||||
|
|
|
@ -155,6 +155,17 @@
|
||||||
#
|
#
|
||||||
# manager-interval: "1m"
|
# manager-interval: "1m"
|
||||||
|
|
||||||
|
# Defines topic names that are not allowed, because they are otherwise used. There are a few default topics
|
||||||
|
# that cannot be used (e.g. app, account, settings, ...). To extend the default list, define them here.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# disallowed-topics:
|
||||||
|
# - about
|
||||||
|
# - pricing
|
||||||
|
# - contact
|
||||||
|
#
|
||||||
|
# disallowed-topics:
|
||||||
|
|
||||||
# Defines if the root route (/) is pointing to the landing page (as on ntfy.sh) or the
|
# Defines if the root route (/) is pointing to the landing page (as on ntfy.sh) or the
|
||||||
# web app. If you self-host, you don't want to change this.
|
# web app. If you self-host, you don't want to change this.
|
||||||
# Can be "app" (default), "home" or "disable" to disable the web app entirely.
|
# Can be "app" (default), "home" or "disable" to disable the web app entirely.
|
||||||
|
|
|
@ -158,6 +158,19 @@ func TestServer_PublishAndSubscribe(t *testing.T) {
|
||||||
require.Equal(t, []string{"tag1", "tag 2", "tag3"}, messages[2].Tags)
|
require.Equal(t, []string{"tag1", "tag 2", "tag3"}, messages[2].Tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServer_Publish_Disallowed_Topic(t *testing.T) {
|
||||||
|
c := newTestConfig(t)
|
||||||
|
c.DisallowedTopics = []string{"about", "time", "this", "got", "added"}
|
||||||
|
s := newTestServer(t, c)
|
||||||
|
|
||||||
|
rr := request(t, s, "PUT", "/mytopic", "my first message", nil)
|
||||||
|
require.Equal(t, 200, rr.Code)
|
||||||
|
|
||||||
|
rr = request(t, s, "PUT", "/about", "another message", nil)
|
||||||
|
require.Equal(t, 400, rr.Code)
|
||||||
|
require.Equal(t, 40010, toHTTPError(t, rr.Body.String()).Code)
|
||||||
|
}
|
||||||
|
|
||||||
func TestServer_StaticSites(t *testing.T) {
|
func TestServer_StaticSites(t *testing.T) {
|
||||||
s := newTestServer(t, newTestConfig(t))
|
s := newTestServer(t, newTestConfig(t))
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import {shortUrl} from "../app/utils";
|
||||||
const routes = {
|
const routes = {
|
||||||
login: "/login",
|
login: "/login",
|
||||||
signup: "/signup",
|
signup: "/signup",
|
||||||
resetPassword: "/reset-password", // Not used (yet)
|
|
||||||
app: config.app_root,
|
app: config.app_root,
|
||||||
account: "/account",
|
account: "/account",
|
||||||
settings: "/settings",
|
settings: "/settings",
|
||||||
|
|
Loading…
Reference in New Issue