Password reset (#448)

* 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
This commit is contained in:
Baptiste Gelez 2019-02-27 13:29:26 +01:00 committed by GitHub
parent e28371bbe4
commit a2b9d7ec44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 927 additions and 15 deletions

93
Cargo.lock generated
View File

@ -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)" = "<none>"
"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)" = "<none>"
"checksum lettre_email 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49)" = "<none>"
"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"

View File

@ -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"]

View File

@ -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 "الكلمات السرية غير متشابهة"

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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 "パスワードが一致しません"

View File

@ -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"

View File

@ -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ę"

View File

@ -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 ""

View File

@ -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"

View File

@ -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 ""

80
src/mail.rs Normal file
View File

@ -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<DebugTransport>;
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<SmtpTransport>;
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> {
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()
}

View File

@ -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<Mutex<Vec<routes::session::ResetRequest>>>>(Arc::new(Mutex::new(vec![])))
.manage(dbpool)
.manage(workpool)
.manage(searcher)

View File

@ -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?<m>")]
pub fn new(user: Option<User>, conn: DbConn, m: Option<String>, intl: I18n) -> Ructe {
@ -76,7 +80,7 @@ pub fn create(conn: DbConn, form: LenientForm<LoginForm>, flash: Option<FlashMes
let uri = Uri::parse(&destination)
.map(|x| x.into_owned())
.map_err(|_| render!(session::login(
.map_err(|_| render!(session::login(
&(&*conn, &intl.catalog, None),
None,
&*form,
@ -101,3 +105,140 @@ pub fn delete(mut cookies: Cookies) -> 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 = "<form>")]
pub fn password_reset_request(
conn: DbConn,
intl: I18n,
mail: State<Arc<Mutex<Mailer>>>,
form: Form<ResetForm>,
requests: State<Arc<Mutex<Vec<ResetRequest>>>>
) -> 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/<token>")]
pub fn password_reset_form(conn: DbConn, intl: I18n, token: String, requests: State<Arc<Mutex<Vec<ResetRequest>>>>) -> Result<Ructe, ErrorPage> {
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/<token>", data = "<form>")]
pub fn password_reset(
conn: DbConn,
intl: I18n,
token: String,
requests: State<Arc<Mutex<Vec<ResetRequest>>>>,
form: Form<NewPasswordForm>
) -> Result<Redirect, Ructe> {
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>(_: 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
}

View File

@ -16,4 +16,5 @@
@input!(ctx.1, password (password), "Password", form, errors, "minlenght=\"1\"")
<input type="submit" value="@i18n!(ctx.1, "Login")" />
</form>
<a href="@uri!(session::password_reset_request_form)">Forgot your password?</a>
})

View File

@ -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"), {}, {}, {
<h1>@i18n!(ctx.1, "Reset your password")</h1>
<form method="POST">
@input!(ctx.1, password (password), "New password", form, errors.clone(), "minlenght=\"8\"")
@input!(ctx.1, password_confirmation (password), "Confirmation", form, errors.clone(), "minlenght=\"8\"")
<input type="submit" value="@i18n!(ctx.1, "Update password")" />
</form>
})

View File

@ -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"), {}, {}, {
<h1>@i18n!(ctx.1, "Reset your password")</h1>
<form method="POST">
@input!(ctx.1, email (email), "E-mail", form, errors.clone(), "minlenght=\"1\"")
<input type="submit" value="@i18n!(ctx.1, "Send reset link")" />
</form>
})

View File

@ -0,0 +1,9 @@
@use template_utils::*;
@use templates::base;
@(ctx: BaseContext)
@:base(ctx, i18n!(ctx.1, "Password reset"), {}, {}, {
<h1>@i18n!(ctx.1, "Check your inbox!")</h1>
<p>@i18n!(ctx.1, "We sent a mail to the address you gave us, with a link to reset your password.")</p>
})