From e4d22ebd8b113eb23964e6344e213dbc5c5ce2ea Mon Sep 17 00:00:00 2001 From: Hunter Kehoe Date: Wed, 3 Apr 2024 21:58:29 -0600 Subject: [PATCH 1/4] allow + in usernames --- user/types.go | 2 +- user/types_test.go | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/user/types.go b/user/types.go index 68ee02f3..6f6b1f69 100644 --- a/user/types.go +++ b/user/types.go @@ -241,7 +241,7 @@ const ( ) var ( - allowedUsernameRegex = regexp.MustCompile(`^[-_.@a-zA-Z0-9]+$`) // Does not include Everyone (*) + allowedUsernameRegex = regexp.MustCompile(`^[-_.+@a-zA-Z0-9]+$`) // Does not include Everyone (*) allowedTopicRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`) // No '*' allowedTopicPatternRegex = regexp.MustCompile(`^[-_*A-Za-z0-9]{1,64}$`) // Adds '*' for wildcards! allowedTierRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`) diff --git a/user/types_test.go b/user/types_test.go index 811d33f2..cd61977b 100644 --- a/user/types_test.go +++ b/user/types_test.go @@ -61,3 +61,15 @@ func TestTierContext(t *testing.T) { require.Equal(t, "price_456", context["stripe_yearly_price_id"]) } + +func TestUsernameRegex(t *testing.T) { + username := "phil" + username_email := "phil@ntfy.sh" + username_email_alias := "phil+alias@ntfy.sh" + username_invalid := "phil\rocks" + + require.True(t, AllowedUsername(username)) + require.True(t, AllowedUsername(username_email)) + require.True(t, AllowedUsername(username_email_alias)) + require.False(t, AllowedUsername(username_invalid)) +} From fc7cf5933facc55d1e68fc6bac16205bfc72982b Mon Sep 17 00:00:00 2001 From: Hunter Kehoe Date: Wed, 3 Apr 2024 21:58:43 -0600 Subject: [PATCH 2/4] fix error message for invalid username/password --- server/errors.go | 1 + server/server_account.go | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/server/errors.go b/server/errors.go index 657d1ddc..5498d12f 100644 --- a/server/errors.go +++ b/server/errors.go @@ -122,6 +122,7 @@ var ( errHTTPBadRequestTemplateInvalid = &errHTTP{40043, http.StatusBadRequest, "invalid request: could not parse template", "https://ntfy.sh/docs/publish/#message-templating", nil} errHTTPBadRequestTemplateDisallowedFunctionCalls = &errHTTP{40044, http.StatusBadRequest, "invalid request: template contains disallowed function calls, e.g. template, call, or define", "https://ntfy.sh/docs/publish/#message-templating", nil} errHTTPBadRequestTemplateExecuteFailed = &errHTTP{40045, http.StatusBadRequest, "invalid request: template execution failed", "https://ntfy.sh/docs/publish/#message-templating", nil} + errHTTPBadRequestInvalidArgument = &errHTTP{40046, http.StatusBadRequest, "invalid request: invalid argument", "", nil} errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", "", nil} errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication", nil} errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication", nil} diff --git a/server/server_account.go b/server/server_account.go index cb841d07..31d581f1 100644 --- a/server/server_account.go +++ b/server/server_account.go @@ -37,7 +37,11 @@ func (s *Server) handleAccountCreate(w http.ResponseWriter, r *http.Request, v * } logvr(v, r).Tag(tagAccount).Field("user_name", newAccount.Username).Info("Creating user %s", newAccount.Username) if err := s.userManager.AddUser(newAccount.Username, newAccount.Password, user.RoleUser); err != nil { - return err + if err.Error() == "invalid argument" { + return errHTTPBadRequestInvalidArgument + } else { + return err + } } v.AccountCreated() return s.writeJSON(w, newSuccessResponse()) From e4c2b938d3465f6400e5cbd53a96da1f84d4fa6a Mon Sep 17 00:00:00 2001 From: Hunter Kehoe Date: Fri, 5 Apr 2024 08:43:56 -0600 Subject: [PATCH 3/4] clean up invalid username code --- server/errors.go | 2 +- server/server_account.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/errors.go b/server/errors.go index 5498d12f..c6076f3f 100644 --- a/server/errors.go +++ b/server/errors.go @@ -122,7 +122,7 @@ var ( errHTTPBadRequestTemplateInvalid = &errHTTP{40043, http.StatusBadRequest, "invalid request: could not parse template", "https://ntfy.sh/docs/publish/#message-templating", nil} errHTTPBadRequestTemplateDisallowedFunctionCalls = &errHTTP{40044, http.StatusBadRequest, "invalid request: template contains disallowed function calls, e.g. template, call, or define", "https://ntfy.sh/docs/publish/#message-templating", nil} errHTTPBadRequestTemplateExecuteFailed = &errHTTP{40045, http.StatusBadRequest, "invalid request: template execution failed", "https://ntfy.sh/docs/publish/#message-templating", nil} - errHTTPBadRequestInvalidArgument = &errHTTP{40046, http.StatusBadRequest, "invalid request: invalid argument", "", nil} + errHTTPBadRequestInvalidUsername = &errHTTP{40046, http.StatusBadRequest, "invalid request: invalid username", "", nil} errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", "", nil} errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication", nil} errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication", nil} diff --git a/server/server_account.go b/server/server_account.go index 31d581f1..3f2368da 100644 --- a/server/server_account.go +++ b/server/server_account.go @@ -2,6 +2,7 @@ package server import ( "encoding/json" + "errors" "heckel.io/ntfy/v2/log" "heckel.io/ntfy/v2/user" "heckel.io/ntfy/v2/util" @@ -37,11 +38,10 @@ func (s *Server) handleAccountCreate(w http.ResponseWriter, r *http.Request, v * } logvr(v, r).Tag(tagAccount).Field("user_name", newAccount.Username).Info("Creating user %s", newAccount.Username) if err := s.userManager.AddUser(newAccount.Username, newAccount.Password, user.RoleUser); err != nil { - if err.Error() == "invalid argument" { - return errHTTPBadRequestInvalidArgument - } else { - return err + if errors.Is(err, user.ErrInvalidArgument) { + return errHTTPBadRequestInvalidUsername } + return err } v.AccountCreated() return s.writeJSON(w, newSuccessResponse()) From 4111bee0c433d158d7a93a2b0d895e24b9395f42 Mon Sep 17 00:00:00 2001 From: Hunter Kehoe Date: Sun, 7 Apr 2024 21:40:24 -0600 Subject: [PATCH 4/4] fix linting issue --- user/types_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/user/types_test.go b/user/types_test.go index cd61977b..690fddf3 100644 --- a/user/types_test.go +++ b/user/types_test.go @@ -64,12 +64,12 @@ func TestTierContext(t *testing.T) { func TestUsernameRegex(t *testing.T) { username := "phil" - username_email := "phil@ntfy.sh" - username_email_alias := "phil+alias@ntfy.sh" - username_invalid := "phil\rocks" + usernameEmail := "phil@ntfy.sh" + usernameEmailAlias := "phil+alias@ntfy.sh" + usernameInvalid := "phil\rocks" require.True(t, AllowedUsername(username)) - require.True(t, AllowedUsername(username_email)) - require.True(t, AllowedUsername(username_email_alias)) - require.False(t, AllowedUsername(username_invalid)) + require.True(t, AllowedUsername(usernameEmail)) + require.True(t, AllowedUsername(usernameEmailAlias)) + require.False(t, AllowedUsername(usernameInvalid)) }