From a2b9d7ec44daf91d5b0dde00715d7185b5342c94 Mon Sep 17 00:00:00 2001 From: Baptiste Gelez Date: Wed, 27 Feb 2019 13:29:26 +0100 Subject: [PATCH] Password reset (#448) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Password reset * Various improvements and fixes for password reset - Reorganize src/mail.rs to make it cleaner - add a build_mail function - only make the requests invalid after 2 hours - avoid infintely-growing list of requests by deleting them once completed, or after 24 hours - avoid sending many requests for the same user - validate the password reset form * Avoid locking so many times Fix durations * Remove old requests even if the current one is not valid * Remove unused feature * Also remove the custom_derive and plugin features while we are at it * Forgot a 0 è_é * Avoid panicking while owning a request lock * Use master branch of lettre so that we can build with the latest OpenSSL * Fix the debug mailer --- Cargo.lock | 93 +++++++++++ Cargo.toml | 3 + po/plume/ar.po | 49 +++++- po/plume/de.po | 46 ++++++ po/plume/en.po | 45 ++++++ po/plume/fr.po | 46 ++++++ po/plume/gl.po | 46 ++++++ po/plume/it.po | 46 ++++++ po/plume/ja.po | 49 +++++- po/plume/nb.po | 46 ++++++ po/plume/pl.po | 46 ++++++ po/plume/plume.pot | 43 +++++ po/plume/pt.po | 49 +++++- po/plume/ru.po | 46 ++++++ src/mail.rs | 80 ++++++++++ src/main.rs | 19 ++- src/routes/session.rs | 149 +++++++++++++++++- templates/session/login.rs.html | 1 + templates/session/password_reset.rs.html | 16 ++ .../session/password_reset_request.rs.html | 15 ++ .../session/password_reset_request_ok.rs.html | 9 ++ 21 files changed, 927 insertions(+), 15 deletions(-) create mode 100644 src/mail.rs create mode 100644 templates/session/password_reset.rs.html create mode 100644 templates/session/password_reset_request.rs.html create mode 100644 templates/session/password_reset_request_ok.rs.html diff --git a/Cargo.lock b/Cargo.lock index b9241831..d6afec74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,6 +115,11 @@ name = "ascii" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ascii_utils" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "askama_escape" version = "0.1.0" @@ -292,6 +297,11 @@ dependencies = [ "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bufstream" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byte-tools" version = "0.2.0" @@ -744,6 +754,20 @@ name = "either" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "email" +version = "0.0.19" +source = "git+https://github.com/lettre/rust-email#3086d7bcda2c3b3fa4b1297cba216151ce4a3efc" +dependencies = [ + "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "encoding" version = "0.2.33" @@ -852,6 +876,14 @@ name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fast_chemail" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "filetime" version = "0.2.4" @@ -1090,6 +1122,15 @@ name = "hex" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "hostname" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "html5ever" version = "0.22.5" @@ -1288,6 +1329,40 @@ name = "lazycell" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lettre" +version = "0.9.0" +source = "git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49#c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49" +dependencies = [ + "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "fast_chemail 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lettre_email" +version = "0.9.0" +source = "git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49#c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49" +dependencies = [ + "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "email 0.0.19 (git+https://github.com/lettre/rust-email)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lettre 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49)", + "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "levenshtein_automata" version = "0.1.1" @@ -1849,6 +1924,8 @@ dependencies = [ "gettext-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "guid-create 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lettre 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49)", + "lettre_email 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49)", "multipart 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "plume-api 0.2.0", @@ -3366,6 +3443,14 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winutil" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -3400,6 +3485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5fc969a8ce2c9c0c4b0429bb8431544f6658283c8326ba5ff8c762b75369335" +"checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" "checksum askama_escape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "719b48039ffac1564f67d70162109ba9341125cee0096a540e478355b3c724a7" "checksum atom_syndication 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a9a7ab83635ff7a3b04856f4ad95324dccc9b947ab1e790fc5c769ee6d6f60c" "checksum atomicwrites 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a3420b33cdefd3feb223dddc23739fc05cc034eb0f2be792c763e3d89e1eb6e3" @@ -3421,6 +3507,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" "checksum blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3" "checksum buf_redux 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f25c67abbf523ff8457771622fb731ac4a2391439de33bc60febcdee1749c9" +"checksum bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum bytecount 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b92204551573580e078dc80017f36a213eb77a0450e4ddd8cfa0f3f2d1f0178f" "checksum byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96c8b41881888cc08af32d47ac4edd52bc7fa27fef774be47a92443756451304" @@ -3475,6 +3562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" "checksum either 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a39bffec1e2015c5d8a6773cb0cf48d0d758c842398f624c34969071f5499ea7" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" +"checksum email 0.0.19 (git+https://github.com/lettre/rust-email)" = "" "checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" "checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" "checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" @@ -3488,6 +3576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fast_chemail 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4" "checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" @@ -3516,6 +3605,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" "checksum html5ever 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c213fa6a618dc1da552f54f85cba74b05d8e883c92ec4e89067736938084c26e" "checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" "checksum http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1a10e5b573b9a0146545010f50772b9e8b1dd0a256564cc4307694c68832a2f5" @@ -3538,6 +3628,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" +"checksum lettre 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49)" = "" +"checksum lettre_email 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49)" = "" "checksum levenshtein_automata 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73a004f877f468548d8d0ac4977456a249d8fabbdb8416c36db163dfc8f2e8ca" "checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" "checksum libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "bff3ac7d6f23730d3b533c35ed75eef638167634476a499feef16c428d74b57b" @@ -3753,6 +3845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c3b48c9cdec42fb06b3b84b5b087405e1fa1c644a1af3930e4dfafe93de48" "checksum yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" diff --git a/Cargo.toml b/Cargo.toml index 96d04275..d4dee03b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,8 @@ gettext-macros = "0.3" gettext-utils = "0.1" guid-create = "0.1" heck = "0.3.0" +lettre = { git = "https://github.com/lettre/lettre", rev = "c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49" } +lettre_email = { git = "https://github.com/lettre/lettre", rev = "c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49" } num_cpus = "1.0" rocket = "0.4.0" rocket_contrib = { version = "0.4.0", features = ["json"] } @@ -73,6 +75,7 @@ rsass = "0.9" default = ["postgres"] postgres = ["plume-models/postgres", "diesel/postgres"] sqlite = ["plume-models/sqlite", "diesel/sqlite"] +debug-mailer = [] [workspace] members = ["plume-api", "plume-cli", "plume-models", "plume-common", "plume-front"] diff --git a/po/plume/ar.po b/po/plume/ar.po index 3f016e54..d8cf561a 100644 --- a/po/plume/ar.po +++ b/po/plume/ar.po @@ -70,6 +70,22 @@ msgstr "تعديل {0}" msgid "You need to be logged in order to reshare a post" msgstr "يجب عليك تسجيل الدخول قصد الإعجاب بالمنشور" +#, fuzzy +msgid "Password reset" +msgstr "كلمة السر" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +#, fuzzy +msgid "Your password was successfully reset." +msgstr "لا يمكن ترك خانة الكلمة السرية فارغة" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + msgid "You need to be logged in order to access your dashboard" msgstr "يجب عليك أولا تسجيل الدخول للوصول إلى لوح التحكم" @@ -468,6 +484,36 @@ msgstr "الانتقال إلى معرضك" msgid "No more result for your query" msgstr "" +msgid "Reset your password" +msgstr "" + +#, fuzzy +msgid "New password" +msgstr "كلمة السر" + +#, fuzzy +msgid "Confirmation" +msgstr "الإعدادات" + +#, fuzzy +msgid "Update password" +msgstr "تحديث الحساب" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +#, fuzzy +msgid "E-mail" +msgstr "البريد الالكتروني" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "تسجيل الدخول" @@ -796,9 +842,6 @@ msgstr "استخدمها كصورة رمزية" #~ msgid "We need an email or a username to identify you" #~ msgstr "نحن بحاجة إلى عنوان بريد إلكتروني أو اسم مستخدم للتعرف عليك" -#~ msgid "Your password can't be empty" -#~ msgstr "لا يمكن ترك خانة الكلمة السرية فارغة" - #~ msgid "Passwords are not matching" #~ msgstr "الكلمات السرية غير متشابهة" diff --git a/po/plume/de.po b/po/plume/de.po index 2fdc0fc0..02d89818 100644 --- a/po/plume/de.po +++ b/po/plume/de.po @@ -74,6 +74,22 @@ msgstr "Bearbeiten" msgid "You need to be logged in order to reshare a post" msgstr "Du musst eingeloggt sein, um deine Benachrichtigungen zu sehen" +#, fuzzy +msgid "Password reset" +msgstr "Passwort" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +# src/routes/session.rs:199 +msgid "Your password was successfully reset." +msgstr "" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + #, fuzzy msgid "You need to be logged in order to access your dashboard" msgstr "Du musst eingeloggt sein, um dein Dashboard zu sehen" @@ -482,6 +498,36 @@ msgstr "Zu deiner Gallerie" msgid "No more result for your query" msgstr "" +msgid "Reset your password" +msgstr "" + +#, fuzzy +msgid "New password" +msgstr "Passwort" + +#, fuzzy +msgid "Confirmation" +msgstr "Konfiguration" + +#, fuzzy +msgid "Update password" +msgstr "Account aktualisieren" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +#, fuzzy +msgid "E-mail" +msgstr "E-Mail" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "" diff --git a/po/plume/en.po b/po/plume/en.po index 3332bfbf..70745c9d 100644 --- a/po/plume/en.po +++ b/po/plume/en.po @@ -78,6 +78,22 @@ msgstr "" msgid "You need to be logged in order to reshare a post" msgstr "" +# src/routes/session.rs:147 +msgid "Password reset" +msgstr "" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +# src/routes/session.rs:199 +msgid "Your password was successfully reset." +msgstr "" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + # src/routes/user.rs:131 msgid "You need to be logged in order to access your dashboard" msgstr "" @@ -459,6 +475,35 @@ msgstr "" msgid "No more result for your query" msgstr "" +msgid "Reset your password" +msgstr "" + +# src/template_utils.rs:144 +msgid "New password" +msgstr "" + +# src/template_utils.rs:144 +msgid "Confirmation" +msgstr "" + +msgid "Update password" +msgstr "" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +# src/template_utils.rs:144 +msgid "E-mail" +msgstr "" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "" diff --git a/po/plume/fr.po b/po/plume/fr.po index 0542d8d0..229edff5 100644 --- a/po/plume/fr.po +++ b/po/plume/fr.po @@ -76,6 +76,22 @@ msgstr "Modifier {0}" msgid "You need to be logged in order to reshare a post" msgstr "Vous devez vous connecter pour voir vos notifications" +#, fuzzy +msgid "Password reset" +msgstr "Mot de passe" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +# src/routes/session.rs:199 +msgid "Your password was successfully reset." +msgstr "" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + #, fuzzy msgid "You need to be logged in order to access your dashboard" msgstr "Vous devez vous connecter pour accéder à votre tableau de bord" @@ -477,6 +493,36 @@ msgstr "Aucun résultat pour votre recherche" msgid "No more result for your query" msgstr "Plus de résultats pour votre recherche" +msgid "Reset your password" +msgstr "" + +#, fuzzy +msgid "New password" +msgstr "Mot de passe" + +#, fuzzy +msgid "Confirmation" +msgstr "Configuration" + +#, fuzzy +msgid "Update password" +msgstr "Mettre à jour mes informations" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +#, fuzzy +msgid "E-mail" +msgstr "Adresse électronique" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "" diff --git a/po/plume/gl.po b/po/plume/gl.po index b12ebcd4..87a4a580 100644 --- a/po/plume/gl.po +++ b/po/plume/gl.po @@ -73,6 +73,22 @@ msgstr "Editar" msgid "You need to be logged in order to reshare a post" msgstr "Debe estar conectada para ver as súas notificacións" +#, fuzzy +msgid "Password reset" +msgstr "Contrasinal" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +# src/routes/session.rs:199 +msgid "Your password was successfully reset." +msgstr "" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + #, fuzzy msgid "You need to be logged in order to access your dashboard" msgstr "Debe estar conectada para acceder ao seu taboleiro" @@ -477,6 +493,36 @@ msgstr "Ir a súa galería" msgid "No more result for your query" msgstr "" +msgid "Reset your password" +msgstr "" + +#, fuzzy +msgid "New password" +msgstr "Contrasinal" + +#, fuzzy +msgid "Confirmation" +msgstr "Axustes" + +#, fuzzy +msgid "Update password" +msgstr "Actualizar conta" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +#, fuzzy +msgid "E-mail" +msgstr "Correo-e" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "" diff --git a/po/plume/it.po b/po/plume/it.po index 4353996d..fd598ac3 100644 --- a/po/plume/it.po +++ b/po/plume/it.po @@ -73,6 +73,22 @@ msgstr "Modifica" msgid "You need to be logged in order to reshare a post" msgstr "Devi effettuare l'accesso per vedere le tue notifiche" +#, fuzzy +msgid "Password reset" +msgstr "Password" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +# src/routes/session.rs:199 +msgid "Your password was successfully reset." +msgstr "" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + #, fuzzy msgid "You need to be logged in order to access your dashboard" msgstr "Devi effettuare l'accesso per accedere al tuo pannello" @@ -480,6 +496,36 @@ msgstr "Vai alla tua galleria" msgid "No more result for your query" msgstr "" +msgid "Reset your password" +msgstr "" + +#, fuzzy +msgid "New password" +msgstr "Password" + +#, fuzzy +msgid "Confirmation" +msgstr "Configurazione" + +#, fuzzy +msgid "Update password" +msgstr "Aggiorna account" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +#, fuzzy +msgid "E-mail" +msgstr "Email" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "" diff --git a/po/plume/ja.po b/po/plume/ja.po index fff56f0a..27ea4adc 100644 --- a/po/plume/ja.po +++ b/po/plume/ja.po @@ -71,6 +71,22 @@ msgstr "編集" msgid "You need to be logged in order to reshare a post" msgstr "投稿をいいねするにはログインする必要があります" +#, fuzzy +msgid "Password reset" +msgstr "パスワード" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +#, fuzzy +msgid "Your password was successfully reset." +msgstr "パスワードは空にできません" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + msgid "You need to be logged in order to access your dashboard" msgstr "ダッシュボードにアクセスするにはログインする必要があります" @@ -473,6 +489,36 @@ msgstr "ギャラリーを参照" msgid "No more result for your query" msgstr "" +msgid "Reset your password" +msgstr "" + +#, fuzzy +msgid "New password" +msgstr "パスワード" + +#, fuzzy +msgid "Confirmation" +msgstr "設定" + +#, fuzzy +msgid "Update password" +msgstr "アカウントをアップデート" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +#, fuzzy +msgid "E-mail" +msgstr "メールアドレス" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "ログイン" @@ -790,9 +836,6 @@ msgstr "アバターとして使う" #~ msgid "We need an email or a username to identify you" #~ msgstr "あなたを識別するために、メールアドレスかユーザー名が必要です" -#~ msgid "Your password can't be empty" -#~ msgstr "パスワードは空にできません" - #~ msgid "Passwords are not matching" #~ msgstr "パスワードが一致しません" diff --git a/po/plume/nb.po b/po/plume/nb.po index 102fd35f..86bbde06 100644 --- a/po/plume/nb.po +++ b/po/plume/nb.po @@ -76,6 +76,22 @@ msgstr "Kommentér \"{0}\"" msgid "You need to be logged in order to reshare a post" msgstr "Du må være logget inn for å se meldingene dine" +#, fuzzy +msgid "Password reset" +msgstr "Passord" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +# src/routes/session.rs:199 +msgid "Your password was successfully reset." +msgstr "" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + #, fuzzy msgid "You need to be logged in order to access your dashboard" msgstr "Du må være logget inn for å redigere profilen din" @@ -505,6 +521,36 @@ msgstr "" msgid "No more result for your query" msgstr "" +msgid "Reset your password" +msgstr "" + +#, fuzzy +msgid "New password" +msgstr "Passord" + +#, fuzzy +msgid "Confirmation" +msgstr "Oppsett" + +#, fuzzy +msgid "Update password" +msgstr "Oppdater konto" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +#, fuzzy +msgid "E-mail" +msgstr "Epost" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "Logg inn" diff --git a/po/plume/pl.po b/po/plume/pl.po index df49c2b7..06aca272 100644 --- a/po/plume/pl.po +++ b/po/plume/pl.po @@ -63,6 +63,22 @@ msgstr "Edytuj {0}" msgid "You need to be logged in order to reshare a post" msgstr "Musisz się zalogować, aby udostępnić wpis" +#, fuzzy +msgid "Password reset" +msgstr "Hasło" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +# src/routes/session.rs:199 +msgid "Your password was successfully reset." +msgstr "" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + msgid "You need to be logged in order to access your dashboard" msgstr "Musisz się zalogować, aby uzyskać dostęp do panelu" @@ -447,6 +463,36 @@ msgstr "Brak wyników dla tego kryterium" msgid "No more result for your query" msgstr "Nie ma więcej wyników pasujących do tych kryteriów" +msgid "Reset your password" +msgstr "" + +#, fuzzy +msgid "New password" +msgstr "Hasło" + +#, fuzzy +msgid "Confirmation" +msgstr "Konfiguracja" + +#, fuzzy +msgid "Update password" +msgstr "Aktualizuj konto" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +#, fuzzy +msgid "E-mail" +msgstr "Adres e-mail" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "Zaloguj się" diff --git a/po/plume/plume.pot b/po/plume/plume.pot index c359ae06..1605dbd2 100644 --- a/po/plume/plume.pot +++ b/po/plume/plume.pot @@ -76,6 +76,22 @@ msgstr "" msgid "You need to be logged in order to reshare a post" msgstr "" +# src/routes/session.rs:161 +msgid "Password reset" +msgstr "" + +# src/routes/session.rs:162 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +# src/routes/session.rs:222 +msgid "Your password was successfully reset." +msgstr "" + +# src/routes/session.rs:224 +msgid "Sorry, but the link expired. Try again" +msgstr "" + # src/routes/user.rs:131 msgid "You need to be logged in order to access your dashboard" msgstr "" @@ -453,6 +469,33 @@ msgstr "" msgid "No more result for your query" msgstr "" +msgid "Reset your password" +msgstr "" + +# src/template_utils.rs:144 +msgid "New password" +msgstr "" + +# src/template_utils.rs:144 +msgid "Confirmation" +msgstr "" + +msgid "Update password" +msgstr "" + +msgid "Check your inbox!" +msgstr "" + +msgid "We sent a mail to the address you gave us, with a link to reset your password." +msgstr "" + +# src/template_utils.rs:144 +msgid "E-mail" +msgstr "" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "" diff --git a/po/plume/pt.po b/po/plume/pt.po index ef72d65f..8af4f91a 100644 --- a/po/plume/pt.po +++ b/po/plume/pt.po @@ -69,6 +69,22 @@ msgstr "Mudar {0}" msgid "You need to be logged in order to reshare a post" msgstr "Você precisa estar logado para gostar de um post" +#, fuzzy +msgid "Password reset" +msgstr "Senha" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +#, fuzzy +msgid "Your password was successfully reset." +msgstr "Sua senha não pode estar vazia" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + msgid "You need to be logged in order to access your dashboard" msgstr "Você precisa estar autenticado para acessar seu painel de controle" @@ -468,6 +484,36 @@ msgstr "Ir para a sua galeria" msgid "No more result for your query" msgstr "" +msgid "Reset your password" +msgstr "" + +#, fuzzy +msgid "New password" +msgstr "Senha" + +#, fuzzy +msgid "Confirmation" +msgstr "Configuração" + +#, fuzzy +msgid "Update password" +msgstr "Atualizar conta" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +#, fuzzy +msgid "E-mail" +msgstr "Email" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "Entrar" @@ -771,9 +817,6 @@ msgstr "Utilizar como avatar" #~ msgstr "" #~ "Precisamos de um endereço de e-mail ou nome de usuário para identificá-lo" -#~ msgid "Your password can't be empty" -#~ msgstr "Sua senha não pode estar vazia" - #~ msgid "Passwords are not matching" #~ msgstr "As senhas não correspondem" diff --git a/po/plume/ru.po b/po/plume/ru.po index 66fbf9f5..c846267b 100644 --- a/po/plume/ru.po +++ b/po/plume/ru.po @@ -75,6 +75,22 @@ msgstr "Редактировать" msgid "You need to be logged in order to reshare a post" msgstr "Вы должны войти чтобы просматривать ваши уведомления" +#, fuzzy +msgid "Password reset" +msgstr "Пароль" + +# src/routes/session.rs:156 +msgid "Here is the link to reset your password: {0}" +msgstr "" + +# src/routes/session.rs:199 +msgid "Your password was successfully reset." +msgstr "" + +# src/routes/session.rs:214 +msgid "Sorry, but the link expired. Try again" +msgstr "" + #, fuzzy msgid "You need to be logged in order to access your dashboard" msgstr "Вы должны войти чтобы получить доступ к вашей панели управления" @@ -484,6 +500,36 @@ msgstr "Перейти в вашу галерею" msgid "No more result for your query" msgstr "" +msgid "Reset your password" +msgstr "" + +#, fuzzy +msgid "New password" +msgstr "Пароль" + +#, fuzzy +msgid "Confirmation" +msgstr "Конфигурация" + +#, fuzzy +msgid "Update password" +msgstr "Обновить аккаунт" + +msgid "Check your inbox!" +msgstr "" + +msgid "" +"We sent a mail to the address you gave us, with a link to reset your " +"password." +msgstr "" + +#, fuzzy +msgid "E-mail" +msgstr "Электронная почта" + +msgid "Send reset link" +msgstr "" + msgid "Login" msgstr "" diff --git a/src/mail.rs b/src/mail.rs new file mode 100644 index 00000000..7b645cac --- /dev/null +++ b/src/mail.rs @@ -0,0 +1,80 @@ +use lettre_email::Email; +use std::env; + +pub use self::mailer::*; + +#[cfg(feature = "debug-mailer")] +mod mailer { + use lettre::{Transport, SendableEmail}; + use std::{io::Read}; + + pub struct DebugTransport; + + impl<'a> Transport<'a> for DebugTransport { + type Result = Result<(), ()>; + + fn send(&mut self, email: SendableEmail) -> Self::Result { + println!( + "{}: from=<{}> to=<{:?}>\n{:#?}", + email.message_id().to_string(), + email.envelope().from().map(ToString::to_string).unwrap_or_default(), + email.envelope().to().to_vec(), + { + let mut message = String::new(); + email.message().read_to_string(&mut message).map_err(|_| ())?; + message + }, + ); + Ok(()) + } + } + + pub type Mailer = Option; + + pub fn init() -> Mailer { + Some(DebugTransport) + } +} + +#[cfg(not(feature = "debug-mailer"))] +mod mailer { + use lettre::{ + SmtpTransport, + SmtpClient, + smtp::{ + authentication::{Credentials, Mechanism}, + extension::ClientId, + ConnectionReuseParameters, + }, + }; + use std::env; + + pub type Mailer = Option; + + pub fn init() -> Mailer { + let server = env::var("MAIL_SERVER").ok()?; + let helo_name = env::var("MAIL_HELO_NAME").unwrap_or_else(|_| "localhost".to_owned()); + let username = env::var("MAIL_USER").ok()?; + let password = env::var("MAIL_PASSWORD").ok()?; + let mail = SmtpClient::new_simple(&server).unwrap() + .hello_name(ClientId::Domain(helo_name)) + .credentials(Credentials::new(username, password)) + .smtp_utf8(true) + .authentication_mechanism(Mechanism::Plain) + .connection_reuse(ConnectionReuseParameters::NoReuse) + .transport(); + Some(mail) + } +} + +pub fn build_mail(dest: String, subject: String, body: String) -> Option { + Email::builder() + .from(env::var("MAIL_ADDRESS") + .or_else(|_| Ok(format!("{}@{}", env::var("MAIL_USER")?, env::var("MAIL_SERVER")?)) as Result<_, env::VarError>) + .expect("Mail server is not correctly configured")) + .to(dest) + .subject(subject) + .text(body) + .build() + .ok() +} diff --git a/src/main.rs b/src/main.rs index 15e7df5c..995c6d09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -#![feature(custom_derive, plugin, decl_macro, proc_macro_hygiene)] +#![feature(decl_macro, proc_macro_hygiene)] extern crate activitypub; extern crate askama_escape; @@ -15,6 +15,8 @@ extern crate gettext_macros; extern crate gettext_utils; extern crate guid_create; extern crate heck; +extern crate lettre; +extern crate lettre_email; extern crate multipart; extern crate num_cpus; extern crate plume_api; @@ -51,13 +53,14 @@ use plume_models::{ use scheduled_thread_pool::ScheduledThreadPool; use std::env; use std::process::exit; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::Duration; init_i18n!("plume", ar, de, en, es, fr, gl, it, ja, nb, pl, pt, ru); mod api; mod inbox; +mod mail; #[macro_use] mod template_utils; mod routes; @@ -117,6 +120,12 @@ Then try to restart Plume. .limit("forms", form_size * 1024) .limit("json", activity_size * 1024)); + let mail = mail::init(); + if mail.is_none() && config.environment.is_prod() { + println!("Warning: the email server is not configured (or not completely)."); + println!("Please refer to the documentation to see how to configure it."); + } + rocket::custom(config) .mount("/", routes![ routes::blogs::details, @@ -177,6 +186,10 @@ Then try to restart Plume. routes::session::new, routes::session::create, routes::session::delete, + routes::session::password_reset_request_form, + routes::session::password_reset_request, + routes::session::password_reset_form, + routes::session::password_reset, routes::static_files, @@ -222,6 +235,8 @@ Then try to restart Plume. routes::errors::unprocessable_entity, routes::errors::server_error ]) + .manage(Arc::new(Mutex::new(mail))) + .manage::>>>(Arc::new(Mutex::new(vec![]))) .manage(dbpool) .manage(workpool) .manage(searcher) diff --git a/src/routes/session.rs b/src/routes/session.rs index 30ec16f8..998ffd3b 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -1,19 +1,23 @@ +use lettre::Transport; use rocket::{ + State, http::{Cookie, Cookies, SameSite, uri::Uri}, response::Redirect, - request::{LenientForm,FlashMessage} + request::{LenientForm, FlashMessage, Form} }; use rocket::http::ext::IntoOwned; use rocket_i18n::I18n; -use std::borrow::Cow; +use std::{borrow::Cow, sync::{Arc, Mutex}, time::Instant}; use validator::{Validate, ValidationError, ValidationErrors}; use template_utils::Ructe; use plume_models::{ + BASE_URL, Error, db_conn::DbConn, users::{User, AUTH_COOKIE} }; - +use mail::{build_mail, Mailer}; +use routes::errors::ErrorPage; #[get("/login?")] pub fn new(user: Option, conn: DbConn, m: Option, intl: I18n) -> Ructe { @@ -76,7 +80,7 @@ pub fn create(conn: DbConn, form: LenientForm, flash: Option Redirect { } Redirect::to("/") } + +#[derive(Clone)] +pub struct ResetRequest { + pub mail: String, + pub id: String, + pub creation_date: Instant, +} + +impl PartialEq for ResetRequest { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +#[get("/password-reset")] +pub fn password_reset_request_form(conn: DbConn, intl: I18n) -> Ructe { + render!(session::password_reset_request( + &(&*conn, &intl.catalog, None), + &ResetForm::default(), + ValidationErrors::default() + )) +} + +#[derive(FromForm, Validate, Default)] +pub struct ResetForm { + #[validate(email)] + pub email: String, +} + +#[post("/password-reset", data = "
")] +pub fn password_reset_request( + conn: DbConn, + intl: I18n, + mail: State>>, + form: Form, + requests: State>>> +) -> Ructe { + let mut requests = requests.lock().unwrap(); + // Remove outdated requests (more than 1 day old) to avoid the list to grow too much + requests.retain(|r| r.creation_date.elapsed().as_secs() < 24 * 60 * 60); + + if User::find_by_email(&*conn, &form.email).is_ok() && !requests.iter().any(|x| x.mail == form.email.clone()) { + let id = plume_common::utils::random_hex(); + + requests.push(ResetRequest { + mail: form.email.clone(), + id: id.clone(), + creation_date: Instant::now(), + }); + + let link = format!("https://{}/password-reset/{}", *BASE_URL, id); + if let Some(message) = build_mail( + form.email.clone(), + i18n!(intl.catalog, "Password reset"), + i18n!(intl.catalog, "Here is the link to reset your password: {0}"; link) + ) { + match *mail.lock().unwrap() { + Some(ref mut mail) => { mail.send(message.into()).map_err(|_| eprintln!("Couldn't send password reset mail")).ok(); } + None => {} + } + } + } + render!(session::password_reset_request_ok( + &(&*conn, &intl.catalog, None) + )) +} + +#[get("/password-reset/")] +pub fn password_reset_form(conn: DbConn, intl: I18n, token: String, requests: State>>>) -> Result { + requests.lock().unwrap().iter().find(|x| x.id == token.clone()).ok_or(Error::NotFound)?; + Ok(render!(session::password_reset( + &(&*conn, &intl.catalog, None), + &NewPasswordForm::default(), + ValidationErrors::default() + ))) +} + +#[derive(FromForm, Default, Validate)] +#[validate( + schema( + function = "passwords_match", + skip_on_field_errors = "false", + message = "Passwords are not matching" + ) +)] +pub struct NewPasswordForm { + pub password: String, + pub password_confirmation: String, +} + +fn passwords_match(form: &NewPasswordForm) -> Result<(), ValidationError> { + if form.password != form.password_confirmation { + Err(ValidationError::new("password_match")) + } else { + Ok(()) + } +} + +#[post("/password-reset/", data = "")] +pub fn password_reset( + conn: DbConn, + intl: I18n, + token: String, + requests: State>>>, + form: Form +) -> Result { + form.validate() + .and_then(|_| { + let mut requests = requests.lock().unwrap(); + let req = requests.iter().find(|x| x.id == token.clone()).ok_or(to_validation(0))?.clone(); + if req.creation_date.elapsed().as_secs() < 60 * 60 * 2 { // Reset link is only valid for 2 hours + requests.retain(|r| *r != req); + let user = User::find_by_email(&*conn, &req.mail).map_err(to_validation)?; + user.reset_password(&*conn, &form.password).ok(); + Ok(Redirect::to(uri!(new: m = i18n!(intl.catalog, "Your password was successfully reset.")))) + } else { + Ok(Redirect::to(uri!(new: m = i18n!(intl.catalog, "Sorry, but the link expired. Try again")))) + } + }) + .map_err(|err| { + render!(session::password_reset( + &(&*conn, &intl.catalog, None), + &form, + err + )) + }) +} + +fn to_validation(_: T) -> ValidationErrors { + let mut errors = ValidationErrors::new(); + errors.add("", ValidationError { + code: Cow::from("server_error"), + message: Some(Cow::from("An unknown error occured")), + params: std::collections::HashMap::new() + }); + errors +} diff --git a/templates/session/login.rs.html b/templates/session/login.rs.html index 14fc799f..a17b907b 100644 --- a/templates/session/login.rs.html +++ b/templates/session/login.rs.html @@ -16,4 +16,5 @@ @input!(ctx.1, password (password), "Password", form, errors, "minlenght=\"1\"") + Forgot your password? }) diff --git a/templates/session/password_reset.rs.html b/templates/session/password_reset.rs.html new file mode 100644 index 00000000..be450cfc --- /dev/null +++ b/templates/session/password_reset.rs.html @@ -0,0 +1,16 @@ +@use template_utils::*; +@use templates::base; +@use routes::session::NewPasswordForm; +@use validator::ValidationErrors; + +@(ctx: BaseContext, form: &NewPasswordForm, errors: ValidationErrors) + +@:base(ctx, i18n!(ctx.1, "Reset your password"), {}, {}, { +

@i18n!(ctx.1, "Reset your password")

+ +
+ @input!(ctx.1, password (password), "New password", form, errors.clone(), "minlenght=\"8\"") + @input!(ctx.1, password_confirmation (password), "Confirmation", form, errors.clone(), "minlenght=\"8\"") + +
+}) diff --git a/templates/session/password_reset_request.rs.html b/templates/session/password_reset_request.rs.html new file mode 100644 index 00000000..b4b0b10d --- /dev/null +++ b/templates/session/password_reset_request.rs.html @@ -0,0 +1,15 @@ +@use template_utils::*; +@use templates::base; +@use routes::session::ResetForm; +@use validator::ValidationErrors; + +@(ctx: BaseContext, form: &ResetForm, errors: ValidationErrors) + +@:base(ctx, i18n!(ctx.1, "Reset your password"), {}, {}, { +

@i18n!(ctx.1, "Reset your password")

+ +
+ @input!(ctx.1, email (email), "E-mail", form, errors.clone(), "minlenght=\"1\"") + +
+}) diff --git a/templates/session/password_reset_request_ok.rs.html b/templates/session/password_reset_request_ok.rs.html new file mode 100644 index 00000000..9b141c89 --- /dev/null +++ b/templates/session/password_reset_request_ok.rs.html @@ -0,0 +1,9 @@ +@use template_utils::*; +@use templates::base; + +@(ctx: BaseContext) + +@:base(ctx, i18n!(ctx.1, "Password reset"), {}, {}, { +

@i18n!(ctx.1, "Check your inbox!")

+

@i18n!(ctx.1, "We sent a mail to the address you gave us, with a link to reset your password.")

+})