Explorar o código

feat(ui): ask for the current password before changing it

Anyone with a signed-in web session could set a new password on the
authentication page, for example with a stolen session cookie or an
unattended browser. Since changing the password now signs out every
other session, this could also lock the owner out of their account.

Ask for the current password whenever a new password is set. Users
created through OAuth2 who have no password yet can still set one
without it, and changing only the username does not require it.
Failed attempts are logged like failed logins.
Frédéric Guillot hai 3 días
pai
achega
f654f7c767

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

@@ -142,6 +142,7 @@
     "error.http_too_many_requests": "أنشأ Miniflux عدداً كبيراً جداً من الطلبات لهذا الموقع. يرجى المحاولة لاحقاً أو تغيير إعدادات التطبيق.",
     "error.http_unexpected_status_code": "الموقع غير متاح حالياً بسبب رمز حالة HTTP غير متوقع: %d. المشكلة ليست من جانب Miniflux. يرجى المحاولة لاحقاً.",
     "error.invalid_categories_sorting_order": "ترتيب فرز الفئات غير صالح.",
+    "error.invalid_current_password": "كلمة المرور الحالية غير صحيحة.",
     "error.invalid_default_home_page": "الصفحة الرئيسية الافتراضية غير صالحة!",
     "error.invalid_display_mode": "وضع عرض تطبيق الويب غير صالح.",
     "error.invalid_entry_direction": "اتجاه المقال غير صالح.",
@@ -419,6 +420,7 @@
     "form.submit.saving": "جارٍ الحفظ...",
     "form.user.label.admin": "مدير",
     "form.user.label.confirmation": "تأكيد كلمة المرور",
+    "form.user.label.current_password": "كلمة المرور الحالية",
     "form.user.label.password": "كلمة المرور",
     "form.user.label.username": "اسم المستخدم",
     "menu.about": "حول",

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

@@ -128,6 +128,7 @@
     "error.http_too_many_requests": "Miniflux hat zu viele Anfragen an diese Webseite gestellt. Bitte versuchen Sie es später erneut oder ändern Sie die Konfiguration der Anwendung.",
     "error.http_unexpected_status_code": "Die Webseite ist aufgrund eines eines unerwarteten HTTP-Fehlers derzeit nicht verfügbar: %d. Das Problem liegt nicht bei Miniflux. Bitte versuchen Sie es später erneut.",
     "error.invalid_categories_sorting_order": "Ungültige Kategorie-Sortierreihenfolge.",
+    "error.invalid_current_password": "Das aktuelle Passwort ist falsch.",
     "error.invalid_default_home_page": "Ungültige Standard-Startseite!",
     "error.invalid_display_mode": "Progressive-Web-App- (PWA-)Anzeigemodus",
     "error.invalid_entry_direction": "Ungültige Sortierreihenfolge.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Speichern...",
     "form.user.label.admin": "Administrator",
     "form.user.label.confirmation": "Passwortbestätigung",
+    "form.user.label.current_password": "Aktuelles Passwort",
     "form.user.label.password": "Passwort",
     "form.user.label.username": "Benutzername",
     "menu.about": "Über",

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

@@ -128,6 +128,7 @@
     "error.http_too_many_requests": "Το Miniflux δημιούργησε πάρα πολλά αιτήματα σε αυτόν τον ιστότοπο. Παρακαλώ δοκιμάστε ξανά αργότερα ή αλλάξτε τη διαμόρφωση της εφαρμογής.",
     "error.http_unexpected_status_code": "Ο ιστότοπος δεν είναι διαθέσιμος αυτήν τη στιγμή λόγω μη αναμενόμενου κωδικού κατάστασης HTTP: %d. Το πρόβλημα δεν είναι στην πλευρά του Miniflux. Παρακαλώ δοκιμάστε ξανά αργότερα.",
     "error.invalid_categories_sorting_order": "Η κατηγορία δεν μπορεί να είναι κενή.",
+    "error.invalid_current_password": "Ο τρέχων κωδικός είναι λανθασμένος.",
     "error.invalid_default_home_page": "Μη έγκυρη προεπιλεγμένη αρχική σελίδα!",
     "error.invalid_display_mode": "Μη έγκυρη λειτουργία εμφάνισης εφαρμογών ιστού.",
     "error.invalid_entry_direction": "Μη έγκυρη κατεύθυνση ταξινόμησης άρθρων.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Αποθήκευση...",
     "form.user.label.admin": "Διαχειριστής",
     "form.user.label.confirmation": "Επιβεβαίωση Κωδικού Πρόσβασης",
+    "form.user.label.current_password": "Τρέχων κωδικός",
     "form.user.label.password": "Κωδικός",
     "form.user.label.username": "Χρήστης",
     "menu.about": "Περί",

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

@@ -130,6 +130,7 @@
     "error.http_too_many_requests": "Miniflux generated too many requests to this website. Please, try again later or change the application configuration.",
     "error.http_unexpected_status_code": "The website is not available at the moment due to an unexpected HTTP status code: %d. The problem is not on Miniflux side. Please, try again later.",
     "error.invalid_categories_sorting_order": "Invalid categories sorting order.",
+    "error.invalid_current_password": "The current password is incorrect.",
     "error.invalid_default_home_page": "Invalid default homepage!",
     "error.invalid_display_mode": "Invalid web app display mode.",
     "error.invalid_entry_direction": "Invalid entry direction.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Saving…",
     "form.user.label.admin": "Administrator",
     "form.user.label.confirmation": "Password Confirmation",
+    "form.user.label.current_password": "Current Password",
     "form.user.label.password": "Password",
     "form.user.label.username": "Username",
     "menu.about": "About",

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

@@ -128,6 +128,7 @@
     "error.http_too_many_requests": "Miniflux generó demasiadas solicitudes a este sitio web. Por favor, inténtalo de nuevo más tarde o cambia la configuración de la aplicación.",
     "error.http_unexpected_status_code": "El sitio web no está disponible en este momento debido a un código de estado HTTP inesperado: %d. El problema no está en el lado de Miniflux. Por favor, inténtalo de nuevo más tarde.",
     "error.invalid_categories_sorting_order": "Orden de clasificación de categorías no válido.",
+    "error.invalid_current_password": "La contraseña actual es incorrecta.",
     "error.invalid_default_home_page": "¡Página de inicio por defecto no válida!",
     "error.invalid_display_mode": "Modo de visualización de la aplicación web no válido.",
     "error.invalid_entry_direction": "Dirección de artículo no válida.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Guardando...",
     "form.user.label.admin": "Administrador",
     "form.user.label.confirmation": "Confirmación de contraseña",
+    "form.user.label.current_password": "Contraseña actual",
     "form.user.label.password": "Contraseña",
     "form.user.label.username": "Nombre de usuario",
     "menu.about": "Acerca de",

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

@@ -128,6 +128,7 @@
     "error.http_too_many_requests": "Miniflux lähetti liikaa pyyntöjä tälle sivustolle. Yritä myöhemmin uudelleen tai muuta sovelluksen asetuksia.",
     "error.http_unexpected_status_code": "Sivusto ei ole nyt käytettävissä odottamattoman HTTP-tilakoodin %d vuoksi. Ongelma ei ole Minifluxin puolella. Yritä myöhemmin uudelleen.",
     "error.invalid_categories_sorting_order": "Virheellinen kategorioiden lajittelujärjestys.",
+    "error.invalid_current_password": "Nykyinen salasana on väärä.",
     "error.invalid_default_home_page": "Väärä oletusarvoinen kotisivu!",
     "error.invalid_display_mode": "Virheellinen verkkosovelluksen näyttötila.",
     "error.invalid_entry_direction": "Virheellinen merkintäsuunta.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Tallennetaan...",
     "form.user.label.admin": "Ylläpitäjä",
     "form.user.label.confirmation": "Salasanan vahvistus",
+    "form.user.label.current_password": "Nykyinen salasana",
     "form.user.label.password": "Salasana",
     "form.user.label.username": "Käyttäjätunnus",
     "menu.about": "Tietoja",

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

@@ -128,6 +128,7 @@
     "error.http_too_many_requests": "Miniflux a généré trop de requêtes vers ce site web. Veuillez réessayer plus tard ou changez la configuration de l'application.",
     "error.http_unexpected_status_code": "Le site web a répondu avec un code HTTP inattendu : %d. Le problème ne vient pas de Miniflux. Veuillez réessayer plus tard.",
     "error.invalid_categories_sorting_order": "L'ordre de tri des catégories n'est pas valide.",
+    "error.invalid_current_password": "Le mot de passe actuel est incorrect.",
     "error.invalid_default_home_page": "Page d'accueil par défaut invalide !",
     "error.invalid_display_mode": "Mode d'affichage de l'application web non valide.",
     "error.invalid_entry_direction": "Ordre de trie non valide.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Sauvegarde en cours...",
     "form.user.label.admin": "Administrateur",
     "form.user.label.confirmation": "Confirmation du mot de passe",
+    "form.user.label.current_password": "Mot de passe actuel",
     "form.user.label.password": "Mot de passe",
     "form.user.label.username": "Nom d'utilisateur",
     "menu.about": "À propos",

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

@@ -130,6 +130,7 @@
     "error.http_too_many_requests": "Miniflux xerou demasiadas peticións a este servidor. Inténtao máis tarde ou cambia a configuración da aplicación.",
     "error.http_unexpected_status_code": "O sitio web non está dispoñible neste momento debido a un código de estado HTTP non agardado: %d. O problema non está en Miniflux, inténtao máis tarde.",
     "error.invalid_categories_sorting_order": "Orde por categorías non válido.",
+    "error.invalid_current_password": "O contrasinal actual é incorrecto.",
     "error.invalid_default_home_page": "Páxina de inicio predeterminada non válida!",
     "error.invalid_display_mode": "Modo para mostrar a aplicación web non válido.",
     "error.invalid_entry_direction": "Dirección da entrada non válida.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Gardando…",
     "form.user.label.admin": "Admin",
     "form.user.label.confirmation": "Confirmar contrasinal",
+    "form.user.label.current_password": "Contrasinal actual",
     "form.user.label.password": "Contrasinal",
     "form.user.label.username": "Identificador",
     "menu.about": "Sobre",

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

@@ -128,6 +128,7 @@
     "error.http_too_many_requests": "मिनीफ्लक्स ने इस वेबसाइट पर बहुत अधिक अनुरोध भेजे हैं। कृपया बाद में पुनः प्रयास करें या एप्लिकेशन कॉन्फ़िगरेशन बदलें।",
     "error.http_unexpected_status_code": "अप्रत्याशित HTTP स्थिति कोड %d के कारण वेबसाइट उपलब्ध नहीं है। समस्या मिनीफ्लक्स की तरफ नहीं है। कृपया बाद में पुनः प्रयास करें।",
     "error.invalid_categories_sorting_order": "अमान्य श्रेणी क्रम।",
+    "error.invalid_current_password": "वर्तमान पासवर्ड गलत है।",
     "error.invalid_default_home_page": "अमान्य डिफ़ॉल्ट मुखपृष्ठ!",
     "error.invalid_display_mode": "अमान्य वेब ऐप्लिकेशन प्रदर्शन मोड.",
     "error.invalid_entry_direction": "अमान्य प्रवेश दिशा।",
@@ -407,6 +408,7 @@
     "form.submit.saving": "सहेजा जा रहा है...",
     "form.user.label.admin": "प्रशासक",
     "form.user.label.confirmation": "पासवर्ड पुष्टि",
+    "form.user.label.current_password": "वर्तमान पासवर्ड",
     "form.user.label.password": "पासवर्ड",
     "form.user.label.username": "उपयोगकर्ता नाम",
     "menu.about": "के बारे में",

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

@@ -125,6 +125,7 @@
     "error.http_too_many_requests": "Terlalu banyak koneksi dari Miniflux yang dibuat ke situs ini. Coba lagi nanti atau ubah konfigurasi aplikasi.",
     "error.http_unexpected_status_code": "Situs ini tidak dapat dijangkau saat ini dikarenakan kode status HTTP tak diduga: %d Masalah ini bukan pada sisi Miniflux. Coba lagi nanti.",
     "error.invalid_categories_sorting_order": "Urutan penyortiran kategori tidak valid.",
+    "error.invalid_current_password": "Kata sandi saat ini salah.",
     "error.invalid_default_home_page": "Beranda baku tidak valid!",
     "error.invalid_display_mode": "Mode tampilan aplikasi web tidak valid.",
     "error.invalid_entry_direction": "Urutan entri tidak valid.",
@@ -404,6 +405,7 @@
     "form.submit.saving": "Menyimpan...",
     "form.user.label.admin": "Admin",
     "form.user.label.confirmation": "Konfirmasi Kata Sandi",
+    "form.user.label.current_password": "Kata Sandi Saat Ini",
     "form.user.label.password": "Kata Sandi",
     "form.user.label.username": "Nama Pengguna",
     "menu.about": "Tentang",

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

@@ -128,6 +128,7 @@
     "error.http_too_many_requests": "Miniflux ha generato troppe richieste verso questo sito. Riprova più tardi o modifica la configurazione dell'applicazione.",
     "error.http_unexpected_status_code": "Il sito web non è disponibile a causa di un codice di stato HTTP inatteso: %d. Il problema non è lato Miniflux. Riprova più tardi.",
     "error.invalid_categories_sorting_order": "L'ordinamento delle categorie non è valido.",
+    "error.invalid_current_password": "La parola d'accesso attuale non è corretta.",
     "error.invalid_default_home_page": "Pagina iniziale predefinita non valida!",
     "error.invalid_display_mode": "Modalità di visualizzazione web app non valida.",
     "error.invalid_entry_direction": "Ordinamento non valido.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Salvataggio in corso...",
     "form.user.label.admin": "Amministratore",
     "form.user.label.confirmation": "Conferma password",
+    "form.user.label.current_password": "Parola d'accesso attuale",
     "form.user.label.password": "Parola d'accesso",
     "form.user.label.username": "Nome utente",
     "menu.about": "Informazioni",

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

@@ -125,6 +125,7 @@
     "error.http_too_many_requests": "Miniflux がこのウェブサイトに対してリクエストを送りすぎました。しばらく待つか、アプリケーション設定を変更してください。",
     "error.http_unexpected_status_code": "予期しない HTTP ステータスコード (%d) により現在このウェブサイトは利用できません。問題は Miniflux 側にはありません。しばらくしてから再度お試しください。",
     "error.invalid_categories_sorting_order": "カテゴリの表示順が無効です。",
+    "error.invalid_current_password": "現在のパスワードが正しくありません。",
     "error.invalid_default_home_page": "デフォルトのトップページが無効です",
     "error.invalid_display_mode": "Web アプリの表示モードが無効です。",
     "error.invalid_entry_direction": "記事の表示順が無効です。",
@@ -404,6 +405,7 @@
     "form.submit.saving": "保存中…",
     "form.user.label.admin": "管理者",
     "form.user.label.confirmation": "パスワード確認",
+    "form.user.label.current_password": "現在のパスワード",
     "form.user.label.password": "パスワード",
     "form.user.label.username": "ユーザー名",
     "menu.about": "ソフトウェア情報",

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

@@ -125,6 +125,7 @@
     "error.http_too_many_requests": "Miniflux가 이 웹사이트에 너무 많은 요청을 보냈습니다. 잠시 기다리거나 애플리케이션 설정을 변경해 주세요.",
     "error.http_unexpected_status_code": "예상치 못한 HTTP 상태 코드(%d)로 인해 현재 이 웹사이트를 사용할 수 없습니다. Miniflux 측의 문제가 아닙니다. 잠시 후 다시 시도해 주세요.",
     "error.invalid_categories_sorting_order": "카테고리 표시 순서가 유효하지 않습니다.",
+    "error.invalid_current_password": "현재 비밀번호가 올바르지 않습니다.",
     "error.invalid_default_home_page": "기본 시작 페이지가 유효하지 않습니다",
     "error.invalid_display_mode": "웹 앱 표시 모드가 유효하지 않습니다.",
     "error.invalid_entry_direction": "게시물 표시 방향이 유효하지 않습니다.",
@@ -404,6 +405,7 @@
     "form.submit.saving": "저장 중…",
     "form.user.label.admin": "관리자",
     "form.user.label.confirmation": "비밀번호 확인",
+    "form.user.label.current_password": "현재 비밀번호",
     "form.user.label.password": "비밀번호",
     "form.user.label.username": "사용자명",
     "menu.about": "소프트웨어 정보",

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

@@ -125,6 +125,7 @@
     "error.http_too_many_requests": "Miniflux tùi chit ê bāng-chām ê chhéng-kiû siuⁿ kè chōe, chhiáⁿ têng chhì-khòaⁿ-māi ah-sī tiâu-chéng thêng-sek siat-tēng.",
     "error.http_unexpected_status_code": "Chit ê bāng-chām chòe liáu chi̍t ê liāu-bōe-tio̍h ê HTTP chōng-thài bé: %d, chhiáⁿ tán--chi̍t-ē chiah koh chhì-khòaⁿ-māi.",
     "error.invalid_categories_sorting_order": "Lūi-pia̍t ê chōe pái bô-hāu, chhiáⁿ tán-hāu %d hun-cheng āu koh chhì-khòaⁿ-māi.",
+    "error.invalid_current_password": "Hiān-chāi ê bi̍t-bé m̄ tio̍h.",
     "error.invalid_default_home_page": "Ū-siat chú-ia̍h ū būn-tôe!",
     "error.invalid_display_mode": "Ū būn-tôe ê su-li̍p bô͘-sek.",
     "error.invalid_entry_direction": "Ū būn-tôe ê su-li̍p hong-hiòng.",
@@ -404,6 +405,7 @@
     "form.submit.saving": "Tng leh pó-chûn…",
     "form.user.label.admin": "Koán-lí-lâng",
     "form.user.label.confirmation": "Koh su-li̍p chi̍t pái bi̍t-bé",
+    "form.user.label.current_password": "Hiān-chāi ê bi̍t-bé",
     "form.user.label.password": "Bi̍t-bé",
     "form.user.label.username": "Kháu-chō miâ",
     "menu.about": "Iú-koan",

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

@@ -128,6 +128,7 @@
     "error.http_too_many_requests": "Miniflux heeft te veel aanvragen gegenereerd voor deze website. Probeer het later nog eens of wijzig de applicatieconfiguratie.",
     "error.http_unexpected_status_code": "De website is momenteel niet beschikbaar vanwege een onverwachte HTTP-statuscode: %d. De oorzaak hiervan ligt niet bij Miniflux. Probeer het later nogmaals aub.",
     "error.invalid_categories_sorting_order": "Ongeldige volgorde van categorieën.",
+    "error.invalid_current_password": "Het huidige wachtwoord is onjuist.",
     "error.invalid_default_home_page": "Ongeldige startpagina!",
     "error.invalid_display_mode": "Ongeldige weergavemodus voor de webapp.",
     "error.invalid_entry_direction": "Ongeldige sorteervolgorde.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Opslaan...",
     "form.user.label.admin": "Beheerder",
     "form.user.label.confirmation": "Bevestig wachtwoord",
+    "form.user.label.current_password": "Huidig wachtwoord",
     "form.user.label.password": "Wachtwoord",
     "form.user.label.username": "Gebruikersnaam",
     "menu.about": "Over",

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

@@ -131,6 +131,7 @@
     "error.http_too_many_requests": "Miniflux wygenerował zbyt wiele żądań do tej witryny. Spróbuj ponownie później lub zmień konfigurację aplikacji.",
     "error.http_unexpected_status_code": "Strona jest w tej chwili niedostępna z powodu nieoczekiwanego kodu stanu HTTP: %d. Problem nie leży po stronie Miniflux. Spróbuj ponownie później.",
     "error.invalid_categories_sorting_order": "Nieprawidłowa kolejność sortowania kategorii.",
+    "error.invalid_current_password": "Obecne hasło jest nieprawidłowe.",
     "error.invalid_default_home_page": "Nieprawidłowa domyślna strona główna!",
     "error.invalid_display_mode": "Nieprawidłowy tryb wyświetlania aplikacji sieciowej.",
     "error.invalid_entry_direction": "Nieprawidłowa kolejność sortowania.",
@@ -410,6 +411,7 @@
     "form.submit.saving": "Zapisywanie…",
     "form.user.label.admin": "Administrator",
     "form.user.label.confirmation": "Potwierdzenie hasła",
+    "form.user.label.current_password": "Obecne hasło",
     "form.user.label.password": "Hasło",
     "form.user.label.username": "Nazwa użytkownika",
     "menu.about": "O czytniku",

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

@@ -128,6 +128,7 @@
     "error.http_too_many_requests": "O Miniflux gerou muitas solicitações para este site. Por favor, tente novamente mais tarde ou altere a configuração do aplicativo.",
     "error.http_unexpected_status_code": "O site não está disponível no momento devido a um código de status HTTP inesperado: %d. O problema não está no Miniflux. Por favor, tente novamente mais tarde.",
     "error.invalid_categories_sorting_order": "A ordem de classificação das categorias não é válida.",
+    "error.invalid_current_password": "A senha atual está incorreta.",
     "error.invalid_default_home_page": "Página inicial por defeito inválida!",
     "error.invalid_display_mode": "Modo de exibição de aplicativo inválido da web.",
     "error.invalid_entry_direction": "Direção de entrada inválida.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Salvando...",
     "form.user.label.admin": "Administrador",
     "form.user.label.confirmation": "Confirmação de senha",
+    "form.user.label.current_password": "Senha atual",
     "form.user.label.password": "Senha",
     "form.user.label.username": "Nome de usuário",
     "menu.about": "Sobre",

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

@@ -131,6 +131,7 @@
     "error.http_too_many_requests": "Miniflux a generat prea multe solicitări pe acest site web. Vă rog, încercați mai tîrziu sau modificați configurațiile aplicației.",
     "error.http_unexpected_status_code": "Acest site web nu este disponibil momentan din cauza unei erori HTTP: %d. Problema nu este de la Miniflux. Vă rugăm să reîncercați mai târziu.",
     "error.invalid_categories_sorting_order": "Ordinea de sortare a categoriilor nu este validă.",
+    "error.invalid_current_password": "Parola curentă este incorectă.",
     "error.invalid_default_home_page": "Pagină de start invalidă!",
     "error.invalid_display_mode": "Mod invalid de afișare în aplicația web.",
     "error.invalid_entry_direction": "Direcție invalidă ăn intrare.",
@@ -410,6 +411,7 @@
     "form.submit.saving": "Salvez…",
     "form.user.label.admin": "Administrator",
     "form.user.label.confirmation": "Confirmare Parolă",
+    "form.user.label.current_password": "Parola curentă",
     "form.user.label.password": "Parolă",
     "form.user.label.username": "Nume utilizator",
     "menu.about": "Despre",

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

@@ -131,6 +131,7 @@
     "error.http_too_many_requests": "Miniflux отправил слишком много запросов к этому сайту. Пожалуйста, попробуйте позже или измените настройки приложения.",
     "error.http_unexpected_status_code": "В данный момент сайт недоступен из-за непредвиденного кода HTTP-ответа: %d. Проблема не связана с Miniflux. Пожалуйста, попробуйте позже.",
     "error.invalid_categories_sorting_order": "Недопустимый порядок сортировки категорий.",
+    "error.invalid_current_password": "Текущий пароль неверен.",
     "error.invalid_default_home_page": "Недопустимая домашняя страница по умолчанию!",
     "error.invalid_display_mode": "Недопустимый режим отображения веб-приложения.",
     "error.invalid_entry_direction": "Недопустимая сортировка записей.",
@@ -410,6 +411,7 @@
     "form.submit.saving": "Сохранение…",
     "form.user.label.admin": "Администратор",
     "form.user.label.confirmation": "Подтверждение пароля",
+    "form.user.label.current_password": "Текущий пароль",
     "form.user.label.password": "Пароль",
     "form.user.label.username": "Имя пользователя",
     "menu.about": "О приложении",

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

@@ -128,6 +128,7 @@
     "error.http_too_many_requests": "Miniflux bu web sitesine çok fazla istek oluşturdu. Lütfen daha sonra tekrar deneyin veya uygulama yapılandırmasını değiştirin.",
     "error.http_unexpected_status_code": "Beklenmeyen bir HTTP durum kodu nedeniyle bu websitesi şu anda kullanılamıyor: %d. Sorun Miniflux tarafında değil. Lütfen daha sonra tekrar deneyiniz.",
     "error.invalid_categories_sorting_order": "Geçersiz kategori sıralama düzeni.",
+    "error.invalid_current_password": "Mevcut parola yanlış.",
     "error.invalid_default_home_page": "Geçersiz varsayılan ana sayfa!",
     "error.invalid_display_mode": "Geçersiz web uygulaması görüntüleme modu.",
     "error.invalid_entry_direction": "Geçersiz makele sıralaması.",
@@ -407,6 +408,7 @@
     "form.submit.saving": "Kaydediliyor...",
     "form.user.label.admin": "Yönetici",
     "form.user.label.confirmation": "Parola Doğrulama",
+    "form.user.label.current_password": "Mevcut Parola",
     "form.user.label.password": "Parola",
     "form.user.label.username": "Kullanıcı Adı",
     "menu.about": "Hakkında",

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

@@ -131,6 +131,7 @@
     "error.http_too_many_requests": "Miniflux згенерував надто багато запитів до цього сайту. Будь ласка, спробуйте пізніше або змініть налаштування програми.",
     "error.http_unexpected_status_code": "Сайт наразі недоступний через неочікуваний HTTP-код: %d. Проблема не на стороні Miniflux. Будь ласка, спробуйте пізніше.",
     "error.invalid_categories_sorting_order": "Недійсний порядок сортування категорій.",
+    "error.invalid_current_password": "Поточний пароль неправильний.",
     "error.invalid_default_home_page": "Недійсна домашня сторінка за замовчуванням!",
     "error.invalid_display_mode": "Недійсний режим відображення.",
     "error.invalid_entry_direction": "Недійсний напрямок запису.",
@@ -410,6 +411,7 @@
     "form.submit.saving": "Зберігаю...",
     "form.user.label.admin": "Адміністратор",
     "form.user.label.confirmation": "Підтверждення паролю",
+    "form.user.label.current_password": "Поточний пароль",
     "form.user.label.password": "Пароль",
     "form.user.label.username": "Ім’я користувача",
     "menu.about": "Про додаток",

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

@@ -125,6 +125,7 @@
     "error.http_too_many_requests": "Miniflux 向此网站生成了过多请求。请稍后重试或更改应用程序配置。",
     "error.http_unexpected_status_code": "由于意外的 HTTP 状态码 %d,网站暂不可用。这不是 Miniflux 的问题,请稍后重试。",
     "error.invalid_categories_sorting_order": "无效的分类排序顺序。",
+    "error.invalid_current_password": "当前密码不正确。",
     "error.invalid_default_home_page": "无效的默认主页!",
     "error.invalid_display_mode": "无效的网页应用显示模式。",
     "error.invalid_entry_direction": "无效的条目方向。",
@@ -404,6 +405,7 @@
     "form.submit.saving": "保存中…",
     "form.user.label.admin": "管理员",
     "form.user.label.confirmation": "确认密码",
+    "form.user.label.current_password": "当前密码",
     "form.user.label.password": "密码",
     "form.user.label.username": "用户名",
     "menu.about": "关于",

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

@@ -125,6 +125,7 @@
     "error.http_too_many_requests": "Miniflux 對此網站的請求過多,請稍後重試或調整程式設定。",
     "error.http_unexpected_status_code": "此網站回應了意外的 HTTP 狀態碼:%d,請稍後重試。",
     "error.invalid_categories_sorting_order": "無效的分類排序",
+    "error.invalid_current_password": "目前密碼不正確。",
     "error.invalid_default_home_page": "預設主頁無效!",
     "error.invalid_display_mode": "無效的顯示模式。",
     "error.invalid_entry_direction": "無效的輸入方向。",
@@ -404,6 +405,7 @@
     "form.submit.saving": "儲存中…",
     "form.user.label.admin": "管理員",
     "form.user.label.confirmation": "再次輸入密碼",
+    "form.user.label.current_password": "目前密碼",
     "form.user.label.password": "密碼",
     "form.user.label.username": "使用者名稱",
     "menu.about": "關於",

+ 5 - 0
internal/template/templates/views/authentication.html

@@ -22,6 +22,11 @@
         <label for="form-username">{{ t "form.user.label.username" }}</label>
         <input type="text" name="username" id="form-username" value="{{ .form.Username }}" autocomplete="username" required>
 
+        {{ if .hasPassword }}
+        <label for="form-current-password">{{ t "form.user.label.current_password" }}</label>
+        <input type="password" name="current_password" id="form-current-password" autocomplete="current-password">
+        {{ end }}
+
         <label for="form-password">{{ t "form.user.label.password" }}</label>
         <input type="password" name="password" id="form-password" value="{{ .form.Password }}" autocomplete="new-password">
 

+ 7 - 0
internal/ui/authentication_show.go

@@ -25,8 +25,15 @@ func (h *handler) showAuthenticationPage(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
+	hasPassword, err := h.store.HasPassword(user.ID)
+	if err != nil {
+		response.HTMLServerError(w, r, err)
+		return
+	}
+
 	view := view.New(h.tpl, r)
 	view.Set("form", form.AuthSettingsForm{Username: user.Username})
+	view.Set("hasPassword", hasPassword)
 	view.Set("menu", "settings")
 	view.Set("user", user)
 	navMetadata, _ := h.store.GetNavMetadata(user.ID)

+ 24 - 0
internal/ui/authentication_update.go

@@ -4,6 +4,7 @@
 package ui // import "miniflux.app/v2/internal/ui"
 
 import (
+	"log/slog"
 	"net/http"
 
 	"miniflux.app/v2/internal/config"
@@ -34,10 +35,17 @@ func (h *handler) updateAuthentication(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	hasPassword, err := h.store.HasPassword(user.ID)
+	if err != nil {
+		response.HTMLServerError(w, r, err)
+		return
+	}
+
 	authSettingsForm := form.NewAuthSettingsForm(r)
 
 	view := view.New(h.tpl, r)
 	view.Set("form", authSettingsForm)
+	view.Set("hasPassword", hasPassword)
 	view.Set("menu", "settings")
 	view.Set("user", user)
 	navMetadata, _ := h.store.GetNavMetadata(user.ID)
@@ -52,6 +60,22 @@ func (h *handler) updateAuthentication(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	// Users created through OAuth2 have no password yet, so there is nothing to confirm.
+	if authSettingsForm.Password != "" && hasPassword {
+		if err := h.store.CheckPassword(user.Username, authSettingsForm.CurrentPassword); err != nil {
+			slog.Warn("Incorrect current password on password change",
+				slog.Bool("authentication_failed", true),
+				slog.String("client_ip", request.ClientIP(r)),
+				slog.String("user_agent", r.UserAgent()),
+				slog.String("username", user.Username),
+				slog.Any("error", err),
+			)
+			view.Set("errorMessage", locale.NewLocalizedError("error.invalid_current_password").Translate(user.Language))
+			response.HTML(w, r, view.Render("authentication"))
+			return
+		}
+	}
+
 	userModificationRequest := &model.UserModificationRequest{
 		Username: model.OptionalString(authSettingsForm.Username),
 		Password: model.OptionalString(authSettingsForm.Password),

+ 8 - 6
internal/ui/form/auth_settings.go

@@ -12,9 +12,10 @@ import (
 
 // AuthSettingsForm represents the authentication settings form.
 type AuthSettingsForm struct {
-	Username     string
-	Password     string
-	Confirmation string
+	Username        string
+	CurrentPassword string
+	Password        string
+	Confirmation    string
 }
 
 // Merge updates the fields of the given user.
@@ -51,8 +52,9 @@ func (a *AuthSettingsForm) Validate() *locale.LocalizedError {
 // NewAuthSettingsForm returns a new AuthSettingsForm.
 func NewAuthSettingsForm(r *http.Request) *AuthSettingsForm {
 	return &AuthSettingsForm{
-		Username:     r.FormValue("username"),
-		Password:     r.FormValue("password"),
-		Confirmation: r.FormValue("confirmation"),
+		Username:        r.FormValue("username"),
+		CurrentPassword: r.FormValue("current_password"),
+		Password:        r.FormValue("password"),
+		Confirmation:    r.FormValue("confirmation"),
 	}
 }

+ 21 - 0
internal/ui/form/auth_settings_test.go

@@ -4,6 +4,10 @@
 package form // import "miniflux.app/v2/internal/ui/form"
 
 import (
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"strings"
 	"testing"
 
 	"miniflux.app/v2/internal/model"
@@ -73,3 +77,20 @@ func TestAuthSettingsMergeKeepsPasswordWhenEmpty(t *testing.T) {
 		t.Errorf("Expected password to be unchanged, got %q", user.Password)
 	}
 }
+
+func TestNewAuthSettingsFormReadsCurrentPassword(t *testing.T) {
+	values := url.Values{
+		"username":         {"user"},
+		"current_password": {"old-secret"},
+		"password":         {"new-secret"},
+		"confirmation":     {"new-secret"},
+	}
+	request := httptest.NewRequest(http.MethodPost, "/authentication", strings.NewReader(values.Encode()))
+	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+	form := NewAuthSettingsForm(request)
+
+	if form.CurrentPassword != "old-secret" {
+		t.Errorf("Expected current password to be read from the request, got %q", form.CurrentPassword)
+	}
+}