Browse Source

Newly created access tokens are now lowercase only

binwiederhier 1 year ago
parent
commit
e96e35b40b
4 changed files with 27 additions and 3 deletions
  1. 1 0
      docs/releases.md
  2. 1 1
      user/manager.go
  3. 13 0
      user/manager_test.go
  4. 12 2
      util/util.go

+ 1 - 0
docs/releases.md

@@ -1227,6 +1227,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
 * Support encoding any header as RFC 2047 ([#737](https://github.com/binwiederhier/ntfy/issues/737), thanks to [@cfouche3005](https://github.com/cfouche3005) for reporting)
 * Do not forward poll requests for UnifiedPush messages (no ticket, thanks to NoName for reporting)
 * Fix `ntfy pub %` segfaulting ([#760](https://github.com/binwiederhier/ntfy/issues/760), thanks to [@clesmian](https://github.com/clesmian) for reporting)
+* Newly created access tokens are now lowercase only to fully support `<topic>+<token>@<domain>` email syntax ([#773](https://github.com/binwiederhier/ntfy/issues/773), thanks to gingervitiz for reporting)
 
 **Maintenance:**
 

+ 1 - 1
user/manager.go

@@ -508,7 +508,7 @@ func (a *Manager) AuthenticateToken(token string) (*User, error) {
 // after a fixed duration unless ChangeToken is called. This function also prunes tokens for the
 // given user, if there are too many of them.
 func (a *Manager) CreateToken(userID, label string, expires time.Time, origin netip.Addr) (*Token, error) {
-	token := util.RandomStringPrefix(tokenPrefix, tokenLength)
+	token := util.RandomLowerStringPrefix(tokenPrefix, tokenLength) // Lowercase only to support "<topic>+<token>@<domain>" email addresses
 	tx, err := a.db.Begin()
 	if err != nil {
 		return nil, err

+ 13 - 0
user/manager_test.go

@@ -183,6 +183,19 @@ func TestManager_MarkUserRemoved_RemoveDeletedUsers(t *testing.T) {
 	require.Equal(t, ErrUserNotFound, err)
 }
 
+func TestManager_CreateToken_Only_Lower(t *testing.T) {
+	a := newTestManager(t, PermissionDenyAll)
+
+	// Create user, add reservations and token
+	require.Nil(t, a.AddUser("user", "pass", RoleAdmin))
+	u, err := a.User("user")
+	require.Nil(t, err)
+
+	token, err := a.CreateToken(u.ID, "", time.Now().Add(time.Hour), netip.IPv4Unspecified())
+	require.Nil(t, err)
+	require.Equal(t, token.Value, strings.ToLower(token.Value))
+}
+
 func TestManager_UserManagement(t *testing.T) {
 	a := newTestManager(t, PermissionDenyAll)
 	require.Nil(t, a.AddUser("phil", "phil", RoleAdmin))

+ 12 - 2
util/util.go

@@ -23,7 +23,8 @@ import (
 )
 
 const (
-	randomStringCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+	randomStringCharset          = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+	randomStringLowerCaseCharset = "abcdefghijklmnopqrstuvwxyz0123456789"
 )
 
 var (
@@ -112,11 +113,20 @@ func RandomString(length int) string {
 
 // RandomStringPrefix returns a random string with a given length, with a prefix
 func RandomStringPrefix(prefix string, length int) string {
+	return randomStringPrefixWithCharset(prefix, length, randomStringCharset)
+}
+
+// RandomLowerStringPrefix returns a random lowercase-only string with a given length, with a prefix
+func RandomLowerStringPrefix(prefix string, length int) string {
+	return randomStringPrefixWithCharset(prefix, length, randomStringLowerCaseCharset)
+}
+
+func randomStringPrefixWithCharset(prefix string, length int, charset string) string {
 	randomMutex.Lock() // Who would have thought that random.Intn() is not thread-safe?!
 	defer randomMutex.Unlock()
 	b := make([]byte, length-len(prefix))
 	for i := range b {
-		b[i] = randomStringCharset[random.Intn(len(randomStringCharset))]
+		b[i] = charset[random.Intn(len(charset))]
 	}
 	return prefix + string(b)
 }