Просмотр исходного кода

fix(validator): validate username format on modification

Username format was only validated when creating a user. Renaming a
user through the API, the authentication settings page, or the admin
user edit form skipped the check, allowing usernames that would be
rejected at creation time.

The admin user edit form now goes through ValidateUserModification
instead of performing its own duplicate check.

The error.invalid_username translation key was also missing from all
locales, so the raw key was displayed instead of a message.
Frédéric Guillot 22 часов назад
Родитель
Сommit
400e6b6a91

+ 1 - 0
internal/locale/translations/ar_SA.json

@@ -154,6 +154,7 @@
     "error.invalid_site_url": "رابط الموقع غير صالح.",
     "error.invalid_theme": "السمة غير صالحة.",
     "error.invalid_timezone": "المنطقة الزمنية غير صالحة.",
+    "error.invalid_username": "اسم المستخدم غير صالح. يُسمح فقط بالأحرف والأرقام والرموز _ - @ .",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux غير قادر على الوصول إلى هذا الموقع بسبب خطأ في الشبكة: %v.",
     "error.network_timeout": "هذا الموقع بطيء جداً وانتهى وقت الطلب: %v",

+ 1 - 0
internal/locale/translations/de_DE.json

@@ -140,6 +140,7 @@
     "error.invalid_site_url": "Ungültiger Site-URL.",
     "error.invalid_theme": "Ungültiges Thema.",
     "error.invalid_timezone": "Ungültige Zeitzone.",
+    "error.invalid_username": "Ungültiger Benutzername. Nur Buchstaben, Ziffern und die Zeichen _ - @ . sind erlaubt.",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux kann die Webseite aufgrund eines Netzwerk-Fehlers nicht erreichen: %v",
     "error.network_timeout": "Die Webseite ist zu langsam und die Anfrage ist abgelaufen: %v.",

+ 1 - 0
internal/locale/translations/el_EL.json

@@ -140,6 +140,7 @@
     "error.invalid_site_url": "Μη έγκυρη διεύθυνση URL ιστότοπου.",
     "error.invalid_theme": "Μη έγκυρο θέμα.",
     "error.invalid_timezone": "Μη έγκυρη ζώνη ώρας.",
+    "error.invalid_username": "Μη έγκυρο όνομα χρήστη. Επιτρέπονται μόνο γράμματα, αριθμοί και οι χαρακτήρες _ - @ .",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Το Miniflux δεν μπορεί να φτάσει σε αυτόν τον ιστότοπο λόγω σφάλματος δικτύου: %v.",
     "error.network_timeout": "Αυτός ο ιστότοπος είναι πολύ αργός και το αίτημα έληξε: %v",

+ 1 - 0
internal/locale/translations/en_US.json

@@ -142,6 +142,7 @@
     "error.invalid_site_url": "Invalid site URL.",
     "error.invalid_theme": "Invalid theme.",
     "error.invalid_timezone": "Invalid timezone.",
+    "error.invalid_username": "Invalid username. Only letters, numbers, and the characters _ - @ . are allowed.",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux is not able to reach this website due to a network error: %v.",
     "error.network_timeout": "This website is too slow and the request timed out: %v",

+ 1 - 0
internal/locale/translations/es_ES.json

@@ -140,6 +140,7 @@
     "error.invalid_site_url": "URL del sitio no válida.",
     "error.invalid_theme": "Tema no válido.",
     "error.invalid_timezone": "Zona horaria no válida.",
+    "error.invalid_username": "Nombre de usuario no válido. Solo se permiten letras, números y los caracteres _ - @ .",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux no puede acceder a este sitio web debido a un error de red: %v.",
     "error.network_timeout": "Este sitio web es demasiado lento y se agotó el tiempo de espera de la solicitud: %v",

+ 1 - 0
internal/locale/translations/fi_FI.json

@@ -140,6 +140,7 @@
     "error.invalid_site_url": "Virheellinen sivuston URL-osoite.",
     "error.invalid_theme": "Virheellinen teema.",
     "error.invalid_timezone": "Virheellinen aikavyöhyke.",
+    "error.invalid_username": "Virheellinen käyttäjänimi. Vain kirjaimet, numerot ja merkit _ - @ . ovat sallittuja.",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux ei tavoita tätä sivustoa verkkovirheen vuoksi: %v.",
     "error.network_timeout": "Tämä sivusto on liian hidas ja pyyntö aikakatkaistiin: %v",

+ 1 - 0
internal/locale/translations/fr_FR.json

@@ -140,6 +140,7 @@
     "error.invalid_site_url": "URL de site non valide.",
     "error.invalid_theme": "Thème non valide.",
     "error.invalid_timezone": "Fuseau horaire non valide.",
+    "error.invalid_username": "Nom d'utilisateur non valide. Seuls les lettres, les chiffres et les caractères _ - @ . sont autorisés.",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux n'est pas en mesure de se connecter à ce site web à cause d'un problème réseau : %v.",
     "error.network_timeout": "Ce site web est trop lent à répondre : %v.",

+ 1 - 0
internal/locale/translations/gl_ES.json

@@ -142,6 +142,7 @@
     "error.invalid_site_url": "URL da web non válido.",
     "error.invalid_theme": "Decorado non válido.",
     "error.invalid_timezone": "Zona horaria non válida.",
+    "error.invalid_username": "Nome de usuario non válido. Só se permiten letras, números e os caracteres _ - @ .",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux non pode acadar esta web por mor dun erro na rede: %v.",
     "error.network_timeout": "Esta web é demasiado lenta e caducou a petición: %v",

+ 1 - 0
internal/locale/translations/hi_IN.json

@@ -140,6 +140,7 @@
     "error.invalid_site_url": "अमान्य साइट यूआरएल",
     "error.invalid_theme": "अमान्य थीम.",
     "error.invalid_timezone": "अमान्य समयक्षेत्र.",
+    "error.invalid_username": "अमान्य उपयोगकर्ता नाम। केवल अक्षर, अंक और _ - @ . वर्णों की अनुमति है।",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "नेटवर्क त्रुटि के कारण मिनीफ्लक्स इस वेबसाइट तक नहीं पहुँच पा रहा: %v.",
     "error.network_timeout": "यह वेबसाइट बहुत धीमी है और अनुरोध का समय समाप्त हो गया: %v",

+ 1 - 0
internal/locale/translations/id_ID.json

@@ -137,6 +137,7 @@
     "error.invalid_site_url": "URL situs tidak valid.",
     "error.invalid_theme": "Tema tidak valid.",
     "error.invalid_timezone": "Zona waktu tidak valid.",
+    "error.invalid_username": "Nama pengguna tidak valid. Hanya huruf, angka, dan karakter _ - @ . yang diizinkan.",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux tidak dapat menjangkau situs ini dikarenakan galat jaringan: %v.",
     "error.network_timeout": "Situs ini terlalu lambat dan permintaan ke situs terlalu lama: %v",

+ 1 - 0
internal/locale/translations/it_IT.json

@@ -140,6 +140,7 @@
     "error.invalid_site_url": "URL del sito non valido.",
     "error.invalid_theme": "Tema non valido.",
     "error.invalid_timezone": "Fuso orario non valido.",
+    "error.invalid_username": "Nome utente non valido. Sono consentiti solo lettere, numeri e i caratteri _ - @ .",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux non riesce a raggiungere questo sito web a causa di un errore di rete: %v.",
     "error.network_timeout": "Questo sito web è troppo lento e la richiesta è scaduta: %v",

+ 1 - 0
internal/locale/translations/ja_JP.json

@@ -137,6 +137,7 @@
     "error.invalid_site_url": "サイト URL が無効です。",
     "error.invalid_theme": "テーマが無効です。",
     "error.invalid_timezone": "タイムゾーンが無効です。",
+    "error.invalid_username": "ユーザー名が無効です。使用できるのは文字、数字、および _ - @ . のみです。",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux はネットワークエラーのためこのウェブサイトに到達できません: %v.",
     "error.network_timeout": "このウェブサイトは応答が遅すぎるためタイムアウトしました: %v",

+ 1 - 0
internal/locale/translations/ko_KR.json

@@ -137,6 +137,7 @@
     "error.invalid_site_url": "사이트 URL이 유효하지 않습니다.",
     "error.invalid_theme": "테마가 유효하지 않습니다.",
     "error.invalid_timezone": "시간대가 유효하지 않습니다.",
+    "error.invalid_username": "사용자명이 유효하지 않습니다. 문자, 숫자 및 _ - @ . 문자만 사용할 수 있습니다.",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "네트워크 오류로 인해 Miniflux가 이 웹사이트에 도달할 수 없습니다: %v.",
     "error.network_timeout": "이 웹사이트의 응답이 너무 느려 시간 초과되었습니다: %v",

+ 1 - 0
internal/locale/translations/nan_Latn_pehoeji.json

@@ -137,6 +137,7 @@
     "error.invalid_site_url": "Siau-sit lâi-goân ê bāng-chām ê bāng-chí ū būn-tôe.",
     "error.invalid_theme": "Ū būn-tôe ê chú-tôe.",
     "error.invalid_timezone": "Ū būn-tôe ê sî-khu.",
+    "error.invalid_username": "Kháu-chō miâ ū būn-tôe. Kan-na ē-sái iōng jī-bó, sò͘-jī kap _ - @ . chit kúi ê jī-hû.",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux bô-hoat-tō͘ liân kàu chit ê bāng-chām, ū khó-lêng sī bāng-lō͘ būn-tôe: %v.",
     "error.network_timeout": "Chit ê bāng-chām ê hôe-èng siuⁿ bān, chhéng-kiû chhiau-kè sî-kan: %v.",

+ 1 - 0
internal/locale/translations/nl_NL.json

@@ -140,6 +140,7 @@
     "error.invalid_site_url": "Ongeldige site URL.",
     "error.invalid_theme": "Ongeldig thema.",
     "error.invalid_timezone": "Ongeldige tijdzone.",
+    "error.invalid_username": "Ongeldige gebruikersnaam. Alleen letters, cijfers en de tekens _ - @ . zijn toegestaan.",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux kan deze website niet bereiken vanwege een netwerkfout: %v.",
     "error.network_timeout": "Deze website is te traag en de aanvraag gaf timeout: %v",

+ 1 - 0
internal/locale/translations/pl_PL.json

@@ -143,6 +143,7 @@
     "error.invalid_site_url": "Nieprawidłowy adres URL witryny.",
     "error.invalid_theme": "Nieprawidłowy motyw.",
     "error.invalid_timezone": "Nieprawidłowa strefa czasowa.",
+    "error.invalid_username": "Nieprawidłowa nazwa użytkownika. Dozwolone są tylko litery, cyfry oraz znaki _ - @ .",
     "error.local_auth_disabled": "Uwierzytelnianie lokalne jest wyłączone. Haseł nie można zmieniać.",
     "error.network_operation": "Miniflux nie może połączyć się z tą witryną z powodu błędu sieci: %v.",
     "error.network_timeout": "Ta witryna internetowa jest zbyt wolna i upłynął limit czasu żądania: %v",

+ 1 - 0
internal/locale/translations/pt_BR.json

@@ -140,6 +140,7 @@
     "error.invalid_site_url": "URL de site inválido.",
     "error.invalid_theme": "Tema inválido.",
     "error.invalid_timezone": "Fuso horário inválido.",
+    "error.invalid_username": "Nome de usuário inválido. Apenas letras, números e os caracteres _ - @ . são permitidos.",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "O Miniflux não conseguiu acessar este site devido a um erro de rede: %v.",
     "error.network_timeout": "Este site está muito lento e a solicitação expirou: %v",

+ 1 - 0
internal/locale/translations/ro_RO.json

@@ -143,6 +143,7 @@
     "error.invalid_site_url": "Adresa URL a site-ului este invalidă.",
     "error.invalid_theme": "Temă invalidă.",
     "error.invalid_timezone": "Dată/oră invalide.",
+    "error.invalid_username": "Nume de utilizator invalid. Sunt permise doar litere, cifre și caracterele _ - @ .",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux nu poate ajunge la acest site din cauza unei erori de rețea: %v.",
     "error.network_timeout": "Acest site web este prea lent și conexiunea nu s-a realizat: %v",

+ 1 - 0
internal/locale/translations/ru_RU.json

@@ -143,6 +143,7 @@
     "error.invalid_site_url": "Недействительный ссылка сайта.",
     "error.invalid_theme": "Недопустимая тема.",
     "error.invalid_timezone": "Недопустимый часовой пояс.",
+    "error.invalid_username": "Недопустимое имя пользователя. Разрешены только буквы, цифры и символы _ - @ .",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux не может открыть сайт из-за ошибки сети: %v.",
     "error.network_timeout": "Этот сайт слишком медленный и время ожидания запроса истекло: %v",

+ 1 - 0
internal/locale/translations/tr_TR.json

@@ -140,6 +140,7 @@
     "error.invalid_site_url": "Geçersiz site URL'si.",
     "error.invalid_theme": "Geçersiz tema.",
     "error.invalid_timezone": "Geçersiz saat dilimi.",
+    "error.invalid_username": "Geçersiz kullanıcı adı. Yalnızca harfler, rakamlar ve _ - @ . karakterlerine izin verilir.",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux bir ağ hatası nedeniyle bu websitesine erişemiyor: %v.",
     "error.network_timeout": "Bu websitesi çok yavaş ve istek zaman aşımına uğradı: %v",

+ 1 - 0
internal/locale/translations/uk_UA.json

@@ -143,6 +143,7 @@
     "error.invalid_site_url": "Недійсна URL-адреса сайту.",
     "error.invalid_theme": "Недійсна тема.",
     "error.invalid_timezone": "Недійсний часовий пояс.",
+    "error.invalid_username": "Недійсне ім’я користувача. Дозволені лише літери, цифри та символи _ - @ .",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux не може отримати доступ до цього сайту через помилку мережі: %v.",
     "error.network_timeout": "Цей сайт занадто повільний і запит перевищив час очікування: %v",

+ 1 - 0
internal/locale/translations/zh_CN.json

@@ -137,6 +137,7 @@
     "error.invalid_site_url": "无效的网站 URL。",
     "error.invalid_theme": "无效的主题。",
     "error.invalid_timezone": "无效的时区。",
+    "error.invalid_username": "无效的用户名。只允许使用字母、数字以及 _ - @ . 字符。",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "由于网络错误,Miniflux 无法访问此网站:%v。",
     "error.network_timeout": "该网站响应过慢,请求已超时:%v",

+ 1 - 0
internal/locale/translations/zh_TW.json

@@ -137,6 +137,7 @@
     "error.invalid_site_url": "Feed 網站的網址無效。",
     "error.invalid_theme": "無效的主題。",
     "error.invalid_timezone": "無效的時區。",
+    "error.invalid_username": "無效的使用者名稱。僅允許使用字母、數字及 _ - @ . 字元。",
     "error.local_auth_disabled": "Local authentication is disabled. Passwords cannot be changed.",
     "error.network_operation": "Miniflux 無法連線到該網站,可能是網路問題:%v。",
     "error.network_timeout": "該網站回應過慢,請求逾時:%v。",

+ 8 - 3
internal/ui/user_update.go

@@ -9,9 +9,10 @@ import (
 
 	"miniflux.app/v2/internal/http/request"
 	"miniflux.app/v2/internal/http/response"
-	"miniflux.app/v2/internal/locale"
+	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/form"
 	"miniflux.app/v2/internal/ui/view"
+	"miniflux.app/v2/internal/validator"
 )
 
 func (h *handler) updateUser(w http.ResponseWriter, r *http.Request) {
@@ -62,8 +63,12 @@ func (h *handler) updateUser(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if h.store.AnotherUserExists(selectedUser.ID, userForm.Username) {
-		view.Set("errorMessage", locale.NewLocalizedError("error.user_already_exists").Translate(loggedUser.Language))
+	userModificationRequest := &model.UserModificationRequest{
+		Username: model.OptionalString(userForm.Username),
+	}
+
+	if validationErr := validator.ValidateUserModification(h.store, selectedUser.ID, userModificationRequest); validationErr != nil {
+		view.Set("errorMessage", validationErr.Translate(loggedUser.Language))
 		response.HTML(w, r, view.Render("edit_user"))
 		return
 	}

+ 7 - 1
internal/validator/user.go

@@ -40,7 +40,13 @@ func ValidateUserModification(store *storage.Storage, userID int64, changes *mod
 	if changes.Username != nil {
 		if *changes.Username == "" {
 			return locale.NewLocalizedError("error.user_mandatory_fields")
-		} else if store.AnotherUserExists(userID, *changes.Username) {
+		}
+
+		if err := validateUsername(*changes.Username); err != nil {
+			return err
+		}
+
+		if store.AnotherUserExists(userID, *changes.Username) {
 			return locale.NewLocalizedError("error.user_already_exists")
 		}
 	}

+ 12 - 0
internal/validator/user_test.go

@@ -259,3 +259,15 @@ func TestValidateUserModificationRejectsPasswordWhenLocalAuthDisabled(t *testing
 		t.Fatalf("expected non-password modification to be accepted, got %v", err)
 	}
 }
+
+func TestValidateUserModificationRejectsInvalidUsername(t *testing.T) {
+	for _, username := range []string{"invalid username", "user/name", "<script>"} {
+		req := &model.UserModificationRequest{
+			Username: new(username),
+		}
+
+		if err := ValidateUserModification(nil, 0, req); err == nil {
+			t.Errorf("expected username %q to be rejected", username)
+		}
+	}
+}