API - display password reset link expiration delay in email
This commit is contained in:
parent
b351f3d42c
commit
075f98e6e5
@ -194,7 +194,7 @@
|
|||||||
<![endif]-->
|
<![endif]-->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<span class="preheader">Use this link to reset your password. The link is only valid for 24 hours.</span>
|
<span class="preheader">Use this link to reset your password. The link is only valid for {{ expiration_delay }}.</span>
|
||||||
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
@ -214,7 +214,7 @@
|
|||||||
<div class="f-fallback">
|
<div class="f-fallback">
|
||||||
<h1>Hi {{username}},</h1>
|
<h1>Hi {{username}},</h1>
|
||||||
<p>You recently requested to reset your password for your account. Use the button below to reset it.
|
<p>You recently requested to reset your password for your account. Use the button below to reset it.
|
||||||
<strong>This password reset is only valid for the next 24 hours.</strong>
|
<strong>This password reset link is only valid for {{ expiration_delay }}.</strong>
|
||||||
</p>
|
</p>
|
||||||
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
Hi {{username}},
|
Hi {{username}},
|
||||||
|
|
||||||
You recently requested to reset your password for your FitTrackee account. Use the button below to reset it. This password reset is only valid for the next 24 hours.
|
You recently requested to reset your password for your FitTrackee account. Use the button below to reset it. This password reset link is only valid for {{ expiration_delay }}.
|
||||||
|
|
||||||
Reset your password ( {{ password_reset_url }} )
|
Reset your password ( {{ password_reset_url }} )
|
||||||
|
|
||||||
|
@ -194,8 +194,7 @@
|
|||||||
<![endif]-->
|
<![endif]-->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<span class="preheader">Use this link to reset your password. The link is only valid for 24 hours.</span>
|
<span class="preheader">Utiliser ce lien pour réinitialiser le mot de passe. Ce lien n'est valide que pendant {{ expiration_delay }}.</span>
|
||||||
<span class="preheader">Utiliser ce lien pour réinitialiser le mot de passe. Ce lien n'est valide que pendant 1 heure.</span>
|
|
||||||
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
@ -216,7 +215,7 @@
|
|||||||
<h1>Bonjour {{username}},</h1>
|
<h1>Bonjour {{username}},</h1>
|
||||||
<p>Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
|
<p>Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
|
||||||
Cliquez sur le bouton ci-dessous pour le réinitialiser.
|
Cliquez sur le bouton ci-dessous pour le réinitialiser.
|
||||||
<strong>Cette réinitialisation n'est valide que pendant 1 heure.</strong>
|
<strong>Cette réinitialisation n'est valide que pendant {{ expiration_delay }}.</strong>
|
||||||
</p>
|
</p>
|
||||||
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Bonjour {{username}},
|
Bonjour {{username}},
|
||||||
|
|
||||||
Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
|
Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
|
||||||
Cliquez sur le lien ci-dessous pour le réinitialiser. Ce lien n'est valide que pendant 1 heure.
|
Cliquez sur le lien ci-dessous pour le réinitialiser. Ce lien n'est valide que pendant {{ expiration_delay }}.
|
||||||
|
|
||||||
Réinitialiser le mot de passe: ( {{ password_reset_url }} )
|
Réinitialiser le mot de passe: ( {{ password_reset_url }} )
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
expected_en_text_body = """Hi test,
|
expected_en_text_body = """Hi test,
|
||||||
|
|
||||||
You recently requested to reset your password for your FitTrackee account. Use the button below to reset it. This password reset is only valid for the next 24 hours.
|
You recently requested to reset your password for your FitTrackee account. Use the button below to reset it. This password reset link is only valid for 3 seconds.
|
||||||
|
|
||||||
Reset your password ( http://localhost/password-reset?token=xxx )
|
Reset your password ( http://localhost/password-reset?token=xxx )
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ The FitTrackee Team"""
|
|||||||
expected_fr_text_body = """Bonjour test,
|
expected_fr_text_body = """Bonjour test,
|
||||||
|
|
||||||
Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
|
Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
|
||||||
Cliquez sur le lien ci-dessous pour le réinitialiser. Ce lien n'est valide que pendant 1 heure.
|
Cliquez sur le lien ci-dessous pour le réinitialiser. Ce lien n'est valide que pendant 3 secondes.
|
||||||
|
|
||||||
Réinitialiser le mot de passe: ( http://localhost/password-reset?token=xxx )
|
Réinitialiser le mot de passe: ( http://localhost/password-reset?token=xxx )
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ Merci,
|
|||||||
L'équipe FitTrackee"""
|
L'équipe FitTrackee"""
|
||||||
|
|
||||||
expected_en_html_body = """ <body>
|
expected_en_html_body = """ <body>
|
||||||
<span class="preheader">Use this link to reset your password. The link is only valid for 24 hours.</span>
|
<span class="preheader">Use this link to reset your password. The link is only valid for 3 seconds.</span>
|
||||||
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
@ -45,7 +45,7 @@ expected_en_html_body = """ <body>
|
|||||||
<div class="f-fallback">
|
<div class="f-fallback">
|
||||||
<h1>Hi test,</h1>
|
<h1>Hi test,</h1>
|
||||||
<p>You recently requested to reset your password for your account. Use the button below to reset it.
|
<p>You recently requested to reset your password for your account. Use the button below to reset it.
|
||||||
<strong>This password reset is only valid for the next 24 hours.</strong>
|
<strong>This password reset link is only valid for 3 seconds.</strong>
|
||||||
</p>
|
</p>
|
||||||
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
@ -99,8 +99,7 @@ expected_en_html_body = """ <body>
|
|||||||
</html>"""
|
</html>"""
|
||||||
|
|
||||||
expected_fr_html_body = """ <body>
|
expected_fr_html_body = """ <body>
|
||||||
<span class="preheader">Use this link to reset your password. The link is only valid for 24 hours.</span>
|
<span class="preheader">Utiliser ce lien pour réinitialiser le mot de passe. Ce lien n'est valide que pendant 3 secondes.</span>
|
||||||
<span class="preheader">Utiliser ce lien pour réinitialiser le mot de passe. Ce lien n'est valide que pendant 1 heure.</span>
|
|
||||||
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
@ -121,7 +120,7 @@ expected_fr_html_body = """ <body>
|
|||||||
<h1>Bonjour test,</h1>
|
<h1>Bonjour test,</h1>
|
||||||
<p>Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
|
<p>Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
|
||||||
Cliquez sur le bouton ci-dessous pour le réinitialiser.
|
Cliquez sur le bouton ci-dessous pour le réinitialiser.
|
||||||
<strong>Cette réinitialisation n'est valide que pendant 1 heure.</strong>
|
<strong>Cette réinitialisation n'est valide que pendant 3 secondes.</strong>
|
||||||
</p>
|
</p>
|
||||||
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -32,6 +32,7 @@ class TestEmailMessage:
|
|||||||
class TestEmailSending:
|
class TestEmailSending:
|
||||||
|
|
||||||
email_data = {
|
email_data = {
|
||||||
|
'expiration_delay': '3 seconds',
|
||||||
'username': 'test',
|
'username': 'test',
|
||||||
'password_reset_url': f'http://localhost/password-reset?token=xxx',
|
'password_reset_url': f'http://localhost/password-reset?token=xxx',
|
||||||
'operating_system': 'Linux',
|
'operating_system': 'Linux',
|
||||||
|
@ -33,6 +33,7 @@ class TestEmailTemplateForPasswordRequest:
|
|||||||
def test_it_gets_text_body(self, app, lang, expected_text_body):
|
def test_it_gets_text_body(self, app, lang, expected_text_body):
|
||||||
email_template = EmailTemplate(app.config.get('TEMPLATES_FOLDER'))
|
email_template = EmailTemplate(app.config.get('TEMPLATES_FOLDER'))
|
||||||
email_data = {
|
email_data = {
|
||||||
|
'expiration_delay': '3 seconds' if lang == 'en' else '3 secondes',
|
||||||
'username': 'test',
|
'username': 'test',
|
||||||
'password_reset_url': f'http://localhost/password-reset?token=xxx',
|
'password_reset_url': f'http://localhost/password-reset?token=xxx',
|
||||||
'operating_system': 'Linux',
|
'operating_system': 'Linux',
|
||||||
@ -48,6 +49,7 @@ class TestEmailTemplateForPasswordRequest:
|
|||||||
def test_it_gets_en_html_body(self, app):
|
def test_it_gets_en_html_body(self, app):
|
||||||
email_template = EmailTemplate(app.config.get('TEMPLATES_FOLDER'))
|
email_template = EmailTemplate(app.config.get('TEMPLATES_FOLDER'))
|
||||||
email_data = {
|
email_data = {
|
||||||
|
'expiration_delay': '3 seconds',
|
||||||
'username': 'test',
|
'username': 'test',
|
||||||
'password_reset_url': f'http://localhost/password-reset?token=xxx',
|
'password_reset_url': f'http://localhost/password-reset?token=xxx',
|
||||||
'operating_system': 'Linux',
|
'operating_system': 'Linux',
|
||||||
@ -63,6 +65,7 @@ class TestEmailTemplateForPasswordRequest:
|
|||||||
def test_it_gets_fr_html_body(self, app):
|
def test_it_gets_fr_html_body(self, app):
|
||||||
email_template = EmailTemplate(app.config.get('TEMPLATES_FOLDER'))
|
email_template = EmailTemplate(app.config.get('TEMPLATES_FOLDER'))
|
||||||
email_data = {
|
email_data = {
|
||||||
|
'expiration_delay': '3 secondes',
|
||||||
'username': 'test',
|
'username': 'test',
|
||||||
'password_reset_url': f'http://localhost/password-reset?token=xxx',
|
'password_reset_url': f'http://localhost/password-reset?token=xxx',
|
||||||
'operating_system': 'Linux',
|
'operating_system': 'Linux',
|
||||||
|
@ -14,6 +14,7 @@ from .utils import (
|
|||||||
authenticate,
|
authenticate,
|
||||||
check_passwords,
|
check_passwords,
|
||||||
display_readable_file_size,
|
display_readable_file_size,
|
||||||
|
get_readable_duration,
|
||||||
register_controls,
|
register_controls,
|
||||||
verify_extension_and_size,
|
verify_extension_and_size,
|
||||||
)
|
)
|
||||||
@ -700,6 +701,10 @@ def request_password_reset():
|
|||||||
password_reset_token = user.encode_password_reset_token(user.id)
|
password_reset_token = user.encode_password_reset_token(user.id)
|
||||||
ui_url = current_app.config['UI_URL']
|
ui_url = current_app.config['UI_URL']
|
||||||
email_data = {
|
email_data = {
|
||||||
|
'expiration_delay': get_readable_duration(
|
||||||
|
current_app.config.get('PASSWORD_TOKEN_EXPIRATION_SECONDS'),
|
||||||
|
'en' if user.language is None else user.language,
|
||||||
|
),
|
||||||
'username': user.username,
|
'username': user.username,
|
||||||
'password_reset_url': (
|
'password_reset_url': (
|
||||||
f'{ui_url}/password-reset?token={password_reset_token.decode()}' # noqa
|
f'{ui_url}/password-reset?token={password_reset_token.decode()}' # noqa
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import re
|
import re
|
||||||
|
from datetime import timedelta
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
import humanize
|
||||||
from flask import current_app, jsonify, request
|
from flask import current_app, jsonify, request
|
||||||
|
|
||||||
from .models import User
|
from .models import User
|
||||||
@ -148,3 +150,12 @@ def display_readable_file_size(size_in_bytes):
|
|||||||
return f"{size_in_bytes:3.1f}{unit}"
|
return f"{size_in_bytes:3.1f}{unit}"
|
||||||
size_in_bytes /= 1024.0
|
size_in_bytes /= 1024.0
|
||||||
return f"{size_in_bytes} bytes"
|
return f"{size_in_bytes} bytes"
|
||||||
|
|
||||||
|
|
||||||
|
def get_readable_duration(duration, locale='en'):
|
||||||
|
if locale != 'en':
|
||||||
|
_t = humanize.i18n.activate(locale) # noqa
|
||||||
|
readable_duration = humanize.naturaldelta(timedelta(seconds=duration))
|
||||||
|
if locale != 'en':
|
||||||
|
humanize.i18n.deactivate()
|
||||||
|
return readable_duration
|
||||||
|
17
fittrackee_api/poetry.lock
generated
17
fittrackee_api/poetry.lock
generated
@ -322,6 +322,17 @@ gevent = ["gevent (>=0.13)"]
|
|||||||
setproctitle = ["setproctitle"]
|
setproctitle = ["setproctitle"]
|
||||||
tornado = ["tornado (>=0.2)"]
|
tornado = ["tornado (>=0.2)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "Python humanize utilities"
|
||||||
|
name = "humanize"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5"
|
||||||
|
version = "2.5.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
tests = ["freezegun", "pytest", "pytest-cov"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||||
@ -995,7 +1006,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
|
|||||||
testing = ["jaraco.itertools", "func-timeout"]
|
testing = ["jaraco.itertools", "func-timeout"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "4199f7c3f0fe738bf7d846017b57e9903aff0b4697a8570be54a9ed63bd20306"
|
content-hash = "344f1311eea15fb17b78dea1fe646a994498814a5ff76374d78f37d0643de282"
|
||||||
python-versions = "^3.7"
|
python-versions = "^3.7"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
@ -1192,6 +1203,10 @@ gunicorn = [
|
|||||||
{file = "gunicorn-20.0.4-py2.py3-none-any.whl", hash = "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"},
|
{file = "gunicorn-20.0.4-py2.py3-none-any.whl", hash = "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"},
|
||||||
{file = "gunicorn-20.0.4.tar.gz", hash = "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626"},
|
{file = "gunicorn-20.0.4.tar.gz", hash = "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626"},
|
||||||
]
|
]
|
||||||
|
humanize = [
|
||||||
|
{file = "humanize-2.5.0-py3-none-any.whl", hash = "sha256:89062c6db8601693b7d223443d0d7529aa9577df43a1387ddd4b9c273abb4a51"},
|
||||||
|
{file = "humanize-2.5.0.tar.gz", hash = "sha256:8a68bd9bccb899fd9bfb1e6d96c1e84e4475551cc9a5b5bdbd69b9b1cfd19c80"},
|
||||||
|
]
|
||||||
idna = [
|
idna = [
|
||||||
{file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"},
|
{file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"},
|
||||||
{file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"},
|
{file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"},
|
||||||
|
@ -18,6 +18,7 @@ pytz = "^2020.1"
|
|||||||
python-forecastio = "^1.4"
|
python-forecastio = "^1.4"
|
||||||
gunicorn = "^20.0"
|
gunicorn = "^20.0"
|
||||||
tqdm = "^4.42"
|
tqdm = "^4.42"
|
||||||
|
humanize = "^2.5.0"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = "^5.3"
|
pytest = "^5.3"
|
||||||
|
Loading…
Reference in New Issue
Block a user