API - display password reset link expiration delay in email
This commit is contained in:
		@@ -194,7 +194,7 @@
 | 
			
		||||
  <![endif]-->
 | 
			
		||||
  </head>
 | 
			
		||||
  <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">
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td align="center">
 | 
			
		||||
@@ -214,7 +214,7 @@
 | 
			
		||||
                      <div class="f-fallback">
 | 
			
		||||
                        <h1>Hi {{username}},</h1>
 | 
			
		||||
                        <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>
 | 
			
		||||
                        <table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
 | 
			
		||||
                          <tr>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
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 }} )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -194,8 +194,7 @@
 | 
			
		||||
  <![endif]-->
 | 
			
		||||
  </head>
 | 
			
		||||
  <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 1 heure.</span>
 | 
			
		||||
    <span class="preheader">Utiliser ce lien pour réinitialiser le mot de passe. Ce lien n'est valide que pendant {{ expiration_delay }}.</span>
 | 
			
		||||
    <table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td align="center">
 | 
			
		||||
@@ -216,7 +215,7 @@
 | 
			
		||||
                        <h1>Bonjour  {{username}},</h1>
 | 
			
		||||
                        <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.
 | 
			
		||||
                          <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>
 | 
			
		||||
                        <table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
 | 
			
		||||
                          <tr>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
Bonjour {{username}},
 | 
			
		||||
 | 
			
		||||
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 }} )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
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 )
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,7 @@ The FitTrackee Team"""
 | 
			
		||||
expected_fr_text_body = """Bonjour test,
 | 
			
		||||
 | 
			
		||||
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 )
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +25,7 @@ Merci,
 | 
			
		||||
L'équipe FitTrackee"""
 | 
			
		||||
 | 
			
		||||
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">
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td align="center">
 | 
			
		||||
@@ -45,7 +45,7 @@ expected_en_html_body = """  <body>
 | 
			
		||||
                      <div class="f-fallback">
 | 
			
		||||
                        <h1>Hi test,</h1>
 | 
			
		||||
                        <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>
 | 
			
		||||
                        <table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
 | 
			
		||||
                          <tr>
 | 
			
		||||
@@ -99,8 +99,7 @@ expected_en_html_body = """  <body>
 | 
			
		||||
</html>"""
 | 
			
		||||
 | 
			
		||||
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 1 heure.</span>
 | 
			
		||||
    <span class="preheader">Utiliser ce lien pour réinitialiser le mot de passe. Ce lien n'est valide que pendant 3 secondes.</span>
 | 
			
		||||
    <table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
 | 
			
		||||
      <tr>
 | 
			
		||||
        <td align="center">
 | 
			
		||||
@@ -121,7 +120,7 @@ expected_fr_html_body = """  <body>
 | 
			
		||||
                        <h1>Bonjour  test,</h1>
 | 
			
		||||
                        <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.
 | 
			
		||||
                          <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>
 | 
			
		||||
                        <table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
 | 
			
		||||
                          <tr>
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ class TestEmailMessage:
 | 
			
		||||
class TestEmailSending:
 | 
			
		||||
 | 
			
		||||
    email_data = {
 | 
			
		||||
        'expiration_delay': '3 seconds',
 | 
			
		||||
        'username': 'test',
 | 
			
		||||
        'password_reset_url': f'http://localhost/password-reset?token=xxx',
 | 
			
		||||
        'operating_system': 'Linux',
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ class TestEmailTemplateForPasswordRequest:
 | 
			
		||||
    def test_it_gets_text_body(self, app, lang, expected_text_body):
 | 
			
		||||
        email_template = EmailTemplate(app.config.get('TEMPLATES_FOLDER'))
 | 
			
		||||
        email_data = {
 | 
			
		||||
            'expiration_delay': '3 seconds' if lang == 'en' else '3 secondes',
 | 
			
		||||
            'username': 'test',
 | 
			
		||||
            'password_reset_url': f'http://localhost/password-reset?token=xxx',
 | 
			
		||||
            'operating_system': 'Linux',
 | 
			
		||||
@@ -48,6 +49,7 @@ class TestEmailTemplateForPasswordRequest:
 | 
			
		||||
    def test_it_gets_en_html_body(self, app):
 | 
			
		||||
        email_template = EmailTemplate(app.config.get('TEMPLATES_FOLDER'))
 | 
			
		||||
        email_data = {
 | 
			
		||||
            'expiration_delay': '3 seconds',
 | 
			
		||||
            'username': 'test',
 | 
			
		||||
            'password_reset_url': f'http://localhost/password-reset?token=xxx',
 | 
			
		||||
            'operating_system': 'Linux',
 | 
			
		||||
@@ -63,6 +65,7 @@ class TestEmailTemplateForPasswordRequest:
 | 
			
		||||
    def test_it_gets_fr_html_body(self, app):
 | 
			
		||||
        email_template = EmailTemplate(app.config.get('TEMPLATES_FOLDER'))
 | 
			
		||||
        email_data = {
 | 
			
		||||
            'expiration_delay': '3 secondes',
 | 
			
		||||
            'username': 'test',
 | 
			
		||||
            'password_reset_url': f'http://localhost/password-reset?token=xxx',
 | 
			
		||||
            'operating_system': 'Linux',
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ from .utils import (
 | 
			
		||||
    authenticate,
 | 
			
		||||
    check_passwords,
 | 
			
		||||
    display_readable_file_size,
 | 
			
		||||
    get_readable_duration,
 | 
			
		||||
    register_controls,
 | 
			
		||||
    verify_extension_and_size,
 | 
			
		||||
)
 | 
			
		||||
@@ -700,6 +701,10 @@ def request_password_reset():
 | 
			
		||||
        password_reset_token = user.encode_password_reset_token(user.id)
 | 
			
		||||
        ui_url = current_app.config['UI_URL']
 | 
			
		||||
        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,
 | 
			
		||||
            'password_reset_url': (
 | 
			
		||||
                f'{ui_url}/password-reset?token={password_reset_token.decode()}'  # noqa
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
import re
 | 
			
		||||
from datetime import timedelta
 | 
			
		||||
from functools import wraps
 | 
			
		||||
 | 
			
		||||
import humanize
 | 
			
		||||
from flask import current_app, jsonify, request
 | 
			
		||||
 | 
			
		||||
from .models import User
 | 
			
		||||
@@ -148,3 +150,12 @@ def display_readable_file_size(size_in_bytes):
 | 
			
		||||
            return f"{size_in_bytes:3.1f}{unit}"
 | 
			
		||||
        size_in_bytes /= 1024.0
 | 
			
		||||
    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"]
 | 
			
		||||
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]]
 | 
			
		||||
category = "main"
 | 
			
		||||
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"]
 | 
			
		||||
 | 
			
		||||
[metadata]
 | 
			
		||||
content-hash = "4199f7c3f0fe738bf7d846017b57e9903aff0b4697a8570be54a9ed63bd20306"
 | 
			
		||||
content-hash = "344f1311eea15fb17b78dea1fe646a994498814a5ff76374d78f37d0643de282"
 | 
			
		||||
python-versions = "^3.7"
 | 
			
		||||
 | 
			
		||||
[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.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 = [
 | 
			
		||||
    {file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"},
 | 
			
		||||
    {file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"},
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ pytz = "^2020.1"
 | 
			
		||||
python-forecastio = "^1.4"
 | 
			
		||||
gunicorn = "^20.0"
 | 
			
		||||
tqdm = "^4.42"
 | 
			
		||||
humanize = "^2.5.0"
 | 
			
		||||
 | 
			
		||||
[tool.poetry.dev-dependencies]
 | 
			
		||||
pytest = "^5.3"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user