aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2022-12-10 17:42:32 +0100
committerDaniel Schadt <kingdread@gmx.de>2022-12-10 17:46:36 +0100
commit67b7f5ae3f20c930f5d633413bf27979c692c6a1 (patch)
tree6336d1697dc43eaec7573423462d64e67f332f0b
parent626c62e547cfa9dfa1fd3b18a524d62b61ccf0d3 (diff)
downloadfietsboek-67b7f5ae3f20c930f5d633413bf27979c692c6a1.tar.gz
fietsboek-67b7f5ae3f20c930f5d633413bf27979c692c6a1.tar.bz2
fietsboek-67b7f5ae3f20c930f5d633413bf27979c692c6a1.zip
add a "Remember me" option for logins
The default session timeout is at 15 minutes, which can be rather short. Therefore, we now have a "Remember me" option, which optionally saves the authentication in a cookie (signed of course, so nobody can tamper with it). This cookie is set to basically never expire, keeping the user logged in while not messing with the session timeout (which is also used for other things like flash messages). We might think about just removing the session authentication completely and doing everything with cookies, but we'll see about that. We definitely want to keep two separate timeouts, but the cookie helper doesn't seem to provide a way to have single cookies last for longer (short of having a second helper like we currently do).
-rw-r--r--fietsboek/__init__.py3
-rw-r--r--fietsboek/locale/de/LC_MESSAGES/messages.mobin11699 -> 11757 bytes
-rw-r--r--fietsboek/locale/de/LC_MESSAGES/messages.po180
-rw-r--r--fietsboek/locale/en/LC_MESSAGES/messages.mobin11018 -> 11069 bytes
-rw-r--r--fietsboek/locale/en/LC_MESSAGES/messages.po180
-rw-r--r--fietsboek/locale/fietslog.pot126
-rw-r--r--fietsboek/security.py30
-rw-r--r--fietsboek/templates/login.jinja210
-rw-r--r--fietsboek/views/default.py11
9 files changed, 298 insertions, 242 deletions
diff --git a/fietsboek/__init__.py b/fietsboek/__init__.py
index a248dc9..d45e9d5 100644
--- a/fietsboek/__init__.py
+++ b/fietsboek/__init__.py
@@ -73,6 +73,7 @@ def main(_global_config, **settings):
return page_manager
my_session_factory = SignedCookieSessionFactory(parsed_config.derive_secret("sessions"))
+ cookie_secret = parsed_config.derive_secret("auth-cookie")
with Configurator(settings=settings) as config:
config.include("pyramid_jinja2")
config.include(".routes")
@@ -82,7 +83,7 @@ def main(_global_config, **settings):
for pack in parsed_config.language_packs:
config.add_translation_dirs(f"{pack}:locale/")
config.set_session_factory(my_session_factory)
- config.set_security_policy(SecurityPolicy())
+ config.set_security_policy(SecurityPolicy(cookie_secret))
config.set_csrf_storage_policy(CookieCSRFStoragePolicy())
config.set_default_csrf_options(require_csrf=True)
config.set_locale_negotiator(locale_negotiator)
diff --git a/fietsboek/locale/de/LC_MESSAGES/messages.mo b/fietsboek/locale/de/LC_MESSAGES/messages.mo
index 7efce7f..7092994 100644
--- a/fietsboek/locale/de/LC_MESSAGES/messages.mo
+++ b/fietsboek/locale/de/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/fietsboek/locale/de/LC_MESSAGES/messages.po b/fietsboek/locale/de/LC_MESSAGES/messages.po
index f4a7f03..dfa2bed 100644
--- a/fietsboek/locale/de/LC_MESSAGES/messages.po
+++ b/fietsboek/locale/de/LC_MESSAGES/messages.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2022-08-10 13:36+0200\n"
+"POT-Creation-Date: 2022-12-10 17:37+0100\n"
"PO-Revision-Date: 2022-07-02 17:35+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
@@ -16,41 +16,41 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.10.3\n"
+"Generated-By: Babel 2.11.0\n"
-#: fietsboek/util.py:282
+#: fietsboek/util.py:274
msgid "password_constraint.mismatch"
msgstr "Passwörter stimmen nicht überein"
-#: fietsboek/util.py:284
+#: fietsboek/util.py:276
msgid "password_constraint.length"
msgstr "Passwort zu kurz"
-#: fietsboek/models/track.py:526
+#: fietsboek/models/track.py:543
msgid "tooltip.table.length"
msgstr "Länge"
-#: fietsboek/models/track.py:527
+#: fietsboek/models/track.py:544
msgid "tooltip.table.uphill"
msgstr "Bergauf"
-#: fietsboek/models/track.py:528
+#: fietsboek/models/track.py:545
msgid "tooltip.table.downhill"
msgstr "Bergab"
-#: fietsboek/models/track.py:529
+#: fietsboek/models/track.py:546
msgid "tooltip.table.moving_time"
msgstr "Fahrzeit"
-#: fietsboek/models/track.py:530
+#: fietsboek/models/track.py:547
msgid "tooltip.table.stopped_time"
msgstr "Haltezeit"
-#: fietsboek/models/track.py:531
+#: fietsboek/models/track.py:549
msgid "tooltip.table.max_speed"
msgstr "Maximalgeschwindigkeit"
-#: fietsboek/models/track.py:533
+#: fietsboek/models/track.py:553
msgid "tooltip.table.avg_speed"
msgstr "Durchschnittsgeschwindigkeit"
@@ -154,43 +154,43 @@ msgstr "Dies ist eine Aufnahme einer Strecke"
msgid "page.browse.synthetic_tooltip"
msgstr "Dies ist eine geplante Strecke"
-#: fietsboek/templates/browse.jinja2:132 fietsboek/templates/details.jinja2:88
+#: fietsboek/templates/browse.jinja2:132 fietsboek/templates/details.jinja2:90
msgid "page.details.date"
msgstr "Datum"
-#: fietsboek/templates/browse.jinja2:134 fietsboek/templates/details.jinja2:102
+#: fietsboek/templates/browse.jinja2:134 fietsboek/templates/details.jinja2:104
msgid "page.details.length"
msgstr "Länge"
-#: fietsboek/templates/browse.jinja2:139 fietsboek/templates/details.jinja2:93
+#: fietsboek/templates/browse.jinja2:139 fietsboek/templates/details.jinja2:95
msgid "page.details.start_time"
msgstr "Startzeit"
-#: fietsboek/templates/browse.jinja2:141 fietsboek/templates/details.jinja2:97
+#: fietsboek/templates/browse.jinja2:141 fietsboek/templates/details.jinja2:99
msgid "page.details.end_time"
msgstr "Endzeit"
-#: fietsboek/templates/browse.jinja2:146 fietsboek/templates/details.jinja2:106
+#: fietsboek/templates/browse.jinja2:146 fietsboek/templates/details.jinja2:108
msgid "page.details.uphill"
msgstr "Bergauf"
-#: fietsboek/templates/browse.jinja2:148 fietsboek/templates/details.jinja2:110
+#: fietsboek/templates/browse.jinja2:148 fietsboek/templates/details.jinja2:112
msgid "page.details.downhill"
msgstr "Bergab"
-#: fietsboek/templates/browse.jinja2:153 fietsboek/templates/details.jinja2:115
+#: fietsboek/templates/browse.jinja2:153 fietsboek/templates/details.jinja2:117
msgid "page.details.moving_time"
msgstr "Fahrzeit"
-#: fietsboek/templates/browse.jinja2:155 fietsboek/templates/details.jinja2:119
+#: fietsboek/templates/browse.jinja2:155 fietsboek/templates/details.jinja2:121
msgid "page.details.stopped_time"
msgstr "Haltezeit"
-#: fietsboek/templates/browse.jinja2:159 fietsboek/templates/details.jinja2:123
+#: fietsboek/templates/browse.jinja2:159 fietsboek/templates/details.jinja2:125
msgid "page.details.max_speed"
msgstr "maximale Geschwindigkeit"
-#: fietsboek/templates/browse.jinja2:161 fietsboek/templates/details.jinja2:127
+#: fietsboek/templates/browse.jinja2:161 fietsboek/templates/details.jinja2:129
msgid "page.details.avg_speed"
msgstr "durchschnittliche Geschwindigkeit"
@@ -208,43 +208,43 @@ msgstr ""
"Es wurden keine Strecken gefunden, auf die Du Zugriff hast. Versuche, "
"Dich anzumelden."
-#: fietsboek/templates/create_account.jinja2:4
+#: fietsboek/templates/create_account.jinja2:5
msgid "page.create_account.title"
msgstr "Konto Erstellen"
-#: fietsboek/templates/create_account.jinja2:11
+#: fietsboek/templates/create_account.jinja2:13
msgid "page.create_account.email_invalid"
msgstr "E-Mail-Adresse ungültig"
-#: fietsboek/templates/create_account.jinja2:13
+#: fietsboek/templates/create_account.jinja2:15
msgid "page.create_account.email"
msgstr "E-Mail-Adresse"
-#: fietsboek/templates/create_account.jinja2:22
+#: fietsboek/templates/create_account.jinja2:24
msgid "page.create_account.name_invalid"
msgstr "Name ungültig"
-#: fietsboek/templates/create_account.jinja2:24
+#: fietsboek/templates/create_account.jinja2:26
msgid "page.create_account.name"
msgstr "Name"
-#: fietsboek/templates/create_account.jinja2:33
+#: fietsboek/templates/create_account.jinja2:35
msgid "page.create_account.password_invalid"
msgstr "Password zu kurz"
-#: fietsboek/templates/create_account.jinja2:35
+#: fietsboek/templates/create_account.jinja2:37
msgid "page.create_account.password"
msgstr "Passwort"
-#: fietsboek/templates/create_account.jinja2:44
+#: fietsboek/templates/create_account.jinja2:46
msgid "page.create_account.password_must_match"
msgstr "Passwörter stimmen nicht überein"
-#: fietsboek/templates/create_account.jinja2:46
+#: fietsboek/templates/create_account.jinja2:48
msgid "page.create_account.repeat_password"
msgstr "Passwort wiederholen"
-#: fietsboek/templates/create_account.jinja2:52
+#: fietsboek/templates/create_account.jinja2:54
msgid "page.create_account.create"
msgstr "Erstellen"
@@ -296,42 +296,42 @@ msgstr "Löschen"
msgid "page.details.delete.close"
msgstr "Abbrechen"
-#: fietsboek/templates/details.jinja2:69
+#: fietsboek/templates/details.jinja2:70
msgid "page.details.tags"
msgstr "Schlagwörter"
-#: fietsboek/templates/details.jinja2:78 fietsboek/templates/edit.jinja2:10
+#: fietsboek/templates/details.jinja2:80 fietsboek/templates/edit.jinja2:10
#: fietsboek/templates/finish_upload.jinja2:10
msgid "page.noscript"
msgstr ""
"JavaScript ist deaktiviert, zum Nutzen aller Funktionen bitte JavaScript "
"aktivieren"
-#: fietsboek/templates/details.jinja2:83
+#: fietsboek/templates/details.jinja2:85
msgid "page.details.download"
msgstr "Herunterladen"
-#: fietsboek/templates/details.jinja2:172
+#: fietsboek/templates/details.jinja2:174
msgid "page.details.comments"
msgstr "Kommentare"
-#: fietsboek/templates/details.jinja2:176
+#: fietsboek/templates/details.jinja2:178
msgid "page.details.comments.author"
msgstr "Kommentar von {}"
-#: fietsboek/templates/details.jinja2:193
+#: fietsboek/templates/details.jinja2:195
msgid "page.details.comments.new.title"
msgstr "Kommentar erstellen"
-#: fietsboek/templates/details.jinja2:196
+#: fietsboek/templates/details.jinja2:198
msgid "page.details.comments.new.input_title"
msgstr "Titel"
-#: fietsboek/templates/details.jinja2:197
+#: fietsboek/templates/details.jinja2:199
msgid "page.details.comments.new.input_comment"
msgstr "Kommentar"
-#: fietsboek/templates/details.jinja2:200
+#: fietsboek/templates/details.jinja2:202
msgid "page.details.comments.new.submit"
msgstr "Absenden"
@@ -401,40 +401,40 @@ msgstr "Vorlage (synthetisch)"
msgid "page.track.form.tags"
msgstr "Schlagwörter"
-#: fietsboek/templates/edit_form.jinja2:43
+#: fietsboek/templates/edit_form.jinja2:50
msgid "page.track.form.add_tag"
msgstr "Schlagwort hinzufügen"
-#: fietsboek/templates/edit_form.jinja2:48
+#: fietsboek/templates/edit_form.jinja2:55
msgid "page.track.form.tagged_people"
msgstr "Markierte Personen"
-#: fietsboek/templates/edit_form.jinja2:63
+#: fietsboek/templates/edit_form.jinja2:70
msgid "page.track.form.add_friend"
msgstr "Freund suchen"
-#: fietsboek/templates/edit_form.jinja2:83
+#: fietsboek/templates/edit_form.jinja2:90
msgid "page.track.form.badges"
msgstr "Wappen"
-#: fietsboek/templates/edit_form.jinja2:94
+#: fietsboek/templates/edit_form.jinja2:101
msgid "page.track.form.description"
msgstr "Beschreibung"
-#: fietsboek/templates/edit_form.jinja2:101
-#: fietsboek/templates/edit_form.jinja2:115
+#: fietsboek/templates/edit_form.jinja2:108
+#: fietsboek/templates/edit_form.jinja2:122
msgid "page.track.form.remove_image"
msgstr "Bild entfernen"
-#: fietsboek/templates/edit_form.jinja2:110
+#: fietsboek/templates/edit_form.jinja2:117
msgid "page.track.form.select_images"
msgstr "Bilder auswählen"
-#: fietsboek/templates/edit_form.jinja2:126
+#: fietsboek/templates/edit_form.jinja2:133
msgid "page.track.form.image_description_modal"
msgstr "Bildbeschreibung"
-#: fietsboek/templates/edit_form.jinja2:133
+#: fietsboek/templates/edit_form.jinja2:140
msgid "page.track.form.image_description_modal.save"
msgstr "Übernehmen"
@@ -460,7 +460,7 @@ msgstr "Startseite"
msgid "page.home.total"
msgstr "Gesamt"
-#: fietsboek/templates/layout.jinja2:40
+#: fietsboek/templates/layout.jinja2:35
msgid "page.navbar.toggle"
msgstr "Navigation umschalten"
@@ -476,31 +476,31 @@ msgstr "Stöbern"
msgid "page.navbar.upload"
msgstr "Hochladen"
-#: fietsboek/templates/layout.jinja2:57
+#: fietsboek/templates/layout.jinja2:62
msgid "page.navbar.user"
msgstr "Nutzer"
-#: fietsboek/templates/layout.jinja2:61
+#: fietsboek/templates/layout.jinja2:66
msgid "page.navbar.welcome_user"
msgstr "Willkommen, {}!"
-#: fietsboek/templates/layout.jinja2:64
+#: fietsboek/templates/layout.jinja2:69
msgid "page.navbar.logout"
msgstr "Abmelden"
-#: fietsboek/templates/layout.jinja2:67
+#: fietsboek/templates/layout.jinja2:72
msgid "page.navbar.profile"
msgstr "Profil"
-#: fietsboek/templates/layout.jinja2:71
+#: fietsboek/templates/layout.jinja2:76
msgid "page.navbar.admin"
msgstr "Admin"
-#: fietsboek/templates/layout.jinja2:77
+#: fietsboek/templates/layout.jinja2:82
msgid "page.navbar.login"
msgstr "Anmelden"
-#: fietsboek/templates/layout.jinja2:81
+#: fietsboek/templates/layout.jinja2:86
msgid "page.navbar.create_account"
msgstr "Konto Erstellen"
@@ -516,11 +516,15 @@ msgstr "E-Mail-Adresse"
msgid "page.login.password"
msgstr "Passwort"
-#: fietsboek/templates/login.jinja2:28
+#: fietsboek/templates/login.jinja2:30
+msgid "page.login.remember-me"
+msgstr "Eingeloggt bleiben"
+
+#: fietsboek/templates/login.jinja2:38
msgid "page.login.submit"
msgstr "Anmelden"
-#: fietsboek/templates/login.jinja2:33
+#: fietsboek/templates/login.jinja2:43
msgid "page.login.forgot_password"
msgstr "Passwort vergessen"
@@ -622,70 +626,70 @@ msgstr "Anfrage senden"
msgid "page.upload.form.gpx"
msgstr "GPX Datei"
-#: fietsboek/views/account.py:48
+#: fietsboek/views/account.py:54
msgid "flash.invalid_name"
msgstr "Ungültiger Name"
-#: fietsboek/views/account.py:53
+#: fietsboek/views/account.py:59
msgid "flash.invalid_email"
msgstr "Ungültige E-Mail-Adresse"
-#: fietsboek/views/account.py:66
+#: fietsboek/views/account.py:72
msgid "email.verify_mail.subject"
msgstr "Fietsboek Konto Bestätigung"
-#: fietsboek/views/account.py:68
+#: fietsboek/views/account.py:75
msgid "email.verify.text"
msgstr ""
"Um Dein Fietsboek-Konto zu bestätigen, nutze diesen Link: {}\n"
"\n"
"Falls Du kein Konto angelegt hast, ignoriere diese E-Mail."
-#: fietsboek/views/account.py:72
+#: fietsboek/views/account.py:86
msgid "flash.a_confirmation_link_has_been_sent"
msgstr "Ein Bestätigungslink wurde versandt"
-#: fietsboek/views/admin.py:45
+#: fietsboek/views/admin.py:49
msgid "flash.badge_added"
msgstr "Wappen hinzugefügt"
-#: fietsboek/views/admin.py:69
+#: fietsboek/views/admin.py:73
msgid "flash.badge_modified"
msgstr "Wappen bearbeitet"
-#: fietsboek/views/admin.py:89
+#: fietsboek/views/admin.py:93
msgid "flash.badge_deleted"
msgstr "Wappen gelöscht"
-#: fietsboek/views/default.py:75
+#: fietsboek/views/default.py:114
msgid "flash.invalid_credentials"
msgstr "Ungültige Nutzerdaten"
-#: fietsboek/views/default.py:79
+#: fietsboek/views/default.py:118
msgid "flash.account_not_verified"
msgstr "Konto noch nicht bestätigt"
-#: fietsboek/views/default.py:82
+#: fietsboek/views/default.py:121
msgid "flash.logged_in"
msgstr "Du bist nun angemeldet"
-#: fietsboek/views/default.py:96
+#: fietsboek/views/default.py:143
msgid "flash.logged_out"
msgstr "Du bist nun abgemeldet"
-#: fietsboek/views/default.py:127
+#: fietsboek/views/default.py:177
msgid "flash.reset_invalid_email"
msgstr "Ungültige E-Mail-Adresse angegeben"
-#: fietsboek/views/default.py:132
+#: fietsboek/views/default.py:182
msgid "flash.password_token_generated"
msgstr "Ein Link zum Zurücksetzen des Passworts wurde versandt"
-#: fietsboek/views/default.py:137
+#: fietsboek/views/default.py:187
msgid "page.password_reset.email.subject"
msgstr "Fietsboek Passwortzurücksetzung"
-#: fietsboek/views/default.py:141
+#: fietsboek/views/default.py:190
msgid "page.password_reset.email.body"
msgstr ""
"Du kannst Dein Fietsboek-Passwort hier zurücksetzen: {}\n"
@@ -693,51 +697,51 @@ msgstr ""
"Falls Du keine Passwortzurücksetzung beantragt hast, dann ignoriere diese"
" E-Mail."
-#: fietsboek/views/default.py:168
+#: fietsboek/views/default.py:223
msgid "flash.email_verified"
msgstr "E-Mail-Adresse bestätigt"
-#: fietsboek/views/default.py:182
+#: fietsboek/views/default.py:237
msgid "flash.password_updated"
msgstr "Passwort aktualisiert"
-#: fietsboek/views/detail.py:94
+#: fietsboek/views/detail.py:101
msgid "flash.track_deleted"
msgstr "Strecke gelöscht"
-#: fietsboek/views/profile.py:57
+#: fietsboek/views/profile.py:61
msgid "flash.personal_data_updated"
msgstr "Persönliche Daten wurden gespeichert"
-#: fietsboek/views/profile.py:77
+#: fietsboek/views/profile.py:79
msgid "flash.friend_not_found"
msgstr "Das angegebene Konto wurde nicht gefunden"
-#: fietsboek/views/profile.py:82
+#: fietsboek/views/profile.py:85
msgid "flash.friend_already_exists"
msgstr "Dieser Freund existiert bereits"
-#: fietsboek/views/profile.py:90
+#: fietsboek/views/profile.py:93
msgid "flash.friend_added"
msgstr "Freund hinzugefügt"
-#: fietsboek/views/profile.py:100
+#: fietsboek/views/profile.py:103
msgid "flash.friend_request_sent"
msgstr "Freundschaftsanfrage gesendet"
-#: fietsboek/views/upload.py:52
+#: fietsboek/views/upload.py:56
msgid "flash.no_file_selected"
msgstr "Keine Datei ausgewählt"
-#: fietsboek/views/upload.py:62
+#: fietsboek/views/upload.py:66
msgid "flash.invalid_file"
msgstr "Ungültige GPX-Datei gesendet"
-#: fietsboek/views/upload.py:179
+#: fietsboek/views/upload.py:189
msgid "flash.upload_success"
msgstr "Hochladen erfolgreich"
-#: fietsboek/views/upload.py:195
+#: fietsboek/views/upload.py:205
msgid "flash.upload_cancelled"
msgstr "Hochladen abgebrochen"
diff --git a/fietsboek/locale/en/LC_MESSAGES/messages.mo b/fietsboek/locale/en/LC_MESSAGES/messages.mo
index ee23eef..7827a62 100644
--- a/fietsboek/locale/en/LC_MESSAGES/messages.mo
+++ b/fietsboek/locale/en/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/fietsboek/locale/en/LC_MESSAGES/messages.po b/fietsboek/locale/en/LC_MESSAGES/messages.po
index 71c8b17..f4b0239 100644
--- a/fietsboek/locale/en/LC_MESSAGES/messages.po
+++ b/fietsboek/locale/en/LC_MESSAGES/messages.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2022-08-10 13:36+0200\n"
+"POT-Creation-Date: 2022-12-10 17:37+0100\n"
"PO-Revision-Date: 2022-06-28 13:11+0200\n"
"Last-Translator: \n"
"Language: en\n"
@@ -16,41 +16,41 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.10.3\n"
+"Generated-By: Babel 2.11.0\n"
-#: fietsboek/util.py:282
+#: fietsboek/util.py:274
msgid "password_constraint.mismatch"
msgstr "Passwords don't match"
-#: fietsboek/util.py:284
+#: fietsboek/util.py:276
msgid "password_constraint.length"
msgstr "Password not long enough"
-#: fietsboek/models/track.py:526
+#: fietsboek/models/track.py:543
msgid "tooltip.table.length"
msgstr "Length"
-#: fietsboek/models/track.py:527
+#: fietsboek/models/track.py:544
msgid "tooltip.table.uphill"
msgstr "Uphill"
-#: fietsboek/models/track.py:528
+#: fietsboek/models/track.py:545
msgid "tooltip.table.downhill"
msgstr "Downhill"
-#: fietsboek/models/track.py:529
+#: fietsboek/models/track.py:546
msgid "tooltip.table.moving_time"
msgstr "Moving Time"
-#: fietsboek/models/track.py:530
+#: fietsboek/models/track.py:547
msgid "tooltip.table.stopped_time"
msgstr "Stopped Time"
-#: fietsboek/models/track.py:531
+#: fietsboek/models/track.py:549
msgid "tooltip.table.max_speed"
msgstr "Max Speed"
-#: fietsboek/models/track.py:533
+#: fietsboek/models/track.py:553
msgid "tooltip.table.avg_speed"
msgstr "Average Speed"
@@ -154,43 +154,43 @@ msgstr "This is a recording of a track"
msgid "page.browse.synthetic_tooltip"
msgstr "This is a pre-planned track"
-#: fietsboek/templates/browse.jinja2:132 fietsboek/templates/details.jinja2:88
+#: fietsboek/templates/browse.jinja2:132 fietsboek/templates/details.jinja2:90
msgid "page.details.date"
msgstr "Date"
-#: fietsboek/templates/browse.jinja2:134 fietsboek/templates/details.jinja2:102
+#: fietsboek/templates/browse.jinja2:134 fietsboek/templates/details.jinja2:104
msgid "page.details.length"
msgstr "Length"
-#: fietsboek/templates/browse.jinja2:139 fietsboek/templates/details.jinja2:93
+#: fietsboek/templates/browse.jinja2:139 fietsboek/templates/details.jinja2:95
msgid "page.details.start_time"
msgstr "Record Start"
-#: fietsboek/templates/browse.jinja2:141 fietsboek/templates/details.jinja2:97
+#: fietsboek/templates/browse.jinja2:141 fietsboek/templates/details.jinja2:99
msgid "page.details.end_time"
msgstr "Record End"
-#: fietsboek/templates/browse.jinja2:146 fietsboek/templates/details.jinja2:106
+#: fietsboek/templates/browse.jinja2:146 fietsboek/templates/details.jinja2:108
msgid "page.details.uphill"
msgstr "Uphill"
-#: fietsboek/templates/browse.jinja2:148 fietsboek/templates/details.jinja2:110
+#: fietsboek/templates/browse.jinja2:148 fietsboek/templates/details.jinja2:112
msgid "page.details.downhill"
msgstr "Downhill"
-#: fietsboek/templates/browse.jinja2:153 fietsboek/templates/details.jinja2:115
+#: fietsboek/templates/browse.jinja2:153 fietsboek/templates/details.jinja2:117
msgid "page.details.moving_time"
msgstr "Moving Time"
-#: fietsboek/templates/browse.jinja2:155 fietsboek/templates/details.jinja2:119
+#: fietsboek/templates/browse.jinja2:155 fietsboek/templates/details.jinja2:121
msgid "page.details.stopped_time"
msgstr "Stopped Time"
-#: fietsboek/templates/browse.jinja2:159 fietsboek/templates/details.jinja2:123
+#: fietsboek/templates/browse.jinja2:159 fietsboek/templates/details.jinja2:125
msgid "page.details.max_speed"
msgstr "Max Speed"
-#: fietsboek/templates/browse.jinja2:161 fietsboek/templates/details.jinja2:127
+#: fietsboek/templates/browse.jinja2:161 fietsboek/templates/details.jinja2:129
msgid "page.details.avg_speed"
msgstr "Average Speed"
@@ -206,43 +206,43 @@ msgstr "No results matching the filters were found."
msgid "page.browse.no_tracks"
msgstr "You currently do not have access to any tracks. Try logging in."
-#: fietsboek/templates/create_account.jinja2:4
+#: fietsboek/templates/create_account.jinja2:5
msgid "page.create_account.title"
msgstr "Create Account"
-#: fietsboek/templates/create_account.jinja2:11
+#: fietsboek/templates/create_account.jinja2:13
msgid "page.create_account.email_invalid"
msgstr "Invalid email address"
-#: fietsboek/templates/create_account.jinja2:13
+#: fietsboek/templates/create_account.jinja2:15
msgid "page.create_account.email"
msgstr "E-Mail"
-#: fietsboek/templates/create_account.jinja2:22
+#: fietsboek/templates/create_account.jinja2:24
msgid "page.create_account.name_invalid"
msgstr "Name invalid"
-#: fietsboek/templates/create_account.jinja2:24
+#: fietsboek/templates/create_account.jinja2:26
msgid "page.create_account.name"
msgstr "Name"
-#: fietsboek/templates/create_account.jinja2:33
+#: fietsboek/templates/create_account.jinja2:35
msgid "page.create_account.password_invalid"
msgstr "Password not long enough"
-#: fietsboek/templates/create_account.jinja2:35
+#: fietsboek/templates/create_account.jinja2:37
msgid "page.create_account.password"
msgstr "Password"
-#: fietsboek/templates/create_account.jinja2:44
+#: fietsboek/templates/create_account.jinja2:46
msgid "page.create_account.password_must_match"
msgstr "Passwords must match"
-#: fietsboek/templates/create_account.jinja2:46
+#: fietsboek/templates/create_account.jinja2:48
msgid "page.create_account.repeat_password"
msgstr "Repeat password"
-#: fietsboek/templates/create_account.jinja2:52
+#: fietsboek/templates/create_account.jinja2:54
msgid "page.create_account.create"
msgstr "Create"
@@ -294,40 +294,40 @@ msgstr "Delete"
msgid "page.details.delete.close"
msgstr "Abort"
-#: fietsboek/templates/details.jinja2:69
+#: fietsboek/templates/details.jinja2:70
msgid "page.details.tags"
msgstr "Tagged as"
-#: fietsboek/templates/details.jinja2:78 fietsboek/templates/edit.jinja2:10
+#: fietsboek/templates/details.jinja2:80 fietsboek/templates/edit.jinja2:10
#: fietsboek/templates/finish_upload.jinja2:10
msgid "page.noscript"
msgstr "JavaScript is disabled, please enable JavaScript"
-#: fietsboek/templates/details.jinja2:83
+#: fietsboek/templates/details.jinja2:85
msgid "page.details.download"
msgstr "Download Tour"
-#: fietsboek/templates/details.jinja2:172
+#: fietsboek/templates/details.jinja2:174
msgid "page.details.comments"
msgstr "Comments"
-#: fietsboek/templates/details.jinja2:176
+#: fietsboek/templates/details.jinja2:178
msgid "page.details.comments.author"
msgstr "Comment by {}"
-#: fietsboek/templates/details.jinja2:193
+#: fietsboek/templates/details.jinja2:195
msgid "page.details.comments.new.title"
msgstr "Create a new comment"
-#: fietsboek/templates/details.jinja2:196
+#: fietsboek/templates/details.jinja2:198
msgid "page.details.comments.new.input_title"
msgstr "Title"
-#: fietsboek/templates/details.jinja2:197
+#: fietsboek/templates/details.jinja2:199
msgid "page.details.comments.new.input_comment"
msgstr "Comment"
-#: fietsboek/templates/details.jinja2:200
+#: fietsboek/templates/details.jinja2:202
msgid "page.details.comments.new.submit"
msgstr "Submit"
@@ -397,40 +397,40 @@ msgstr "Template (synthetic)"
msgid "page.track.form.tags"
msgstr "Tags"
-#: fietsboek/templates/edit_form.jinja2:43
+#: fietsboek/templates/edit_form.jinja2:50
msgid "page.track.form.add_tag"
msgstr "Add Tag"
-#: fietsboek/templates/edit_form.jinja2:48
+#: fietsboek/templates/edit_form.jinja2:55
msgid "page.track.form.tagged_people"
msgstr "Tagged People"
-#: fietsboek/templates/edit_form.jinja2:63
+#: fietsboek/templates/edit_form.jinja2:70
msgid "page.track.form.add_friend"
msgstr "Search friends"
-#: fietsboek/templates/edit_form.jinja2:83
+#: fietsboek/templates/edit_form.jinja2:90
msgid "page.track.form.badges"
msgstr "Badges"
-#: fietsboek/templates/edit_form.jinja2:94
+#: fietsboek/templates/edit_form.jinja2:101
msgid "page.track.form.description"
msgstr "Description"
-#: fietsboek/templates/edit_form.jinja2:101
-#: fietsboek/templates/edit_form.jinja2:115
+#: fietsboek/templates/edit_form.jinja2:108
+#: fietsboek/templates/edit_form.jinja2:122
msgid "page.track.form.remove_image"
msgstr "Remove image"
-#: fietsboek/templates/edit_form.jinja2:110
+#: fietsboek/templates/edit_form.jinja2:117
msgid "page.track.form.select_images"
msgstr "Select images"
-#: fietsboek/templates/edit_form.jinja2:126
+#: fietsboek/templates/edit_form.jinja2:133
msgid "page.track.form.image_description_modal"
msgstr "Image description"
-#: fietsboek/templates/edit_form.jinja2:133
+#: fietsboek/templates/edit_form.jinja2:140
msgid "page.track.form.image_description_modal.save"
msgstr "Apply"
@@ -456,7 +456,7 @@ msgstr "Home"
msgid "page.home.total"
msgstr "Total"
-#: fietsboek/templates/layout.jinja2:40
+#: fietsboek/templates/layout.jinja2:35
msgid "page.navbar.toggle"
msgstr "Toggle navigation"
@@ -472,31 +472,31 @@ msgstr "Browse"
msgid "page.navbar.upload"
msgstr "Upload"
-#: fietsboek/templates/layout.jinja2:57
+#: fietsboek/templates/layout.jinja2:62
msgid "page.navbar.user"
msgstr "User"
-#: fietsboek/templates/layout.jinja2:61
+#: fietsboek/templates/layout.jinja2:66
msgid "page.navbar.welcome_user"
msgstr "Welcome, {}!"
-#: fietsboek/templates/layout.jinja2:64
+#: fietsboek/templates/layout.jinja2:69
msgid "page.navbar.logout"
msgstr "Logout"
-#: fietsboek/templates/layout.jinja2:67
+#: fietsboek/templates/layout.jinja2:72
msgid "page.navbar.profile"
msgstr "Profile"
-#: fietsboek/templates/layout.jinja2:71
+#: fietsboek/templates/layout.jinja2:76
msgid "page.navbar.admin"
msgstr "Admin"
-#: fietsboek/templates/layout.jinja2:77
+#: fietsboek/templates/layout.jinja2:82
msgid "page.navbar.login"
msgstr "Login"
-#: fietsboek/templates/layout.jinja2:81
+#: fietsboek/templates/layout.jinja2:86
msgid "page.navbar.create_account"
msgstr "Create Account"
@@ -512,11 +512,15 @@ msgstr "E-Mail"
msgid "page.login.password"
msgstr "Password"
-#: fietsboek/templates/login.jinja2:28
+#: fietsboek/templates/login.jinja2:30
+msgid "page.login.remember-me"
+msgstr "Remember me"
+
+#: fietsboek/templates/login.jinja2:38
msgid "page.login.submit"
msgstr "Login"
-#: fietsboek/templates/login.jinja2:33
+#: fietsboek/templates/login.jinja2:43
msgid "page.login.forgot_password"
msgstr "Forgot password"
@@ -618,121 +622,121 @@ msgstr "Send request"
msgid "page.upload.form.gpx"
msgstr "GPX file"
-#: fietsboek/views/account.py:48
+#: fietsboek/views/account.py:54
msgid "flash.invalid_name"
msgstr "Invalid name"
-#: fietsboek/views/account.py:53
+#: fietsboek/views/account.py:59
msgid "flash.invalid_email"
msgstr "Invalid email"
-#: fietsboek/views/account.py:66
+#: fietsboek/views/account.py:72
msgid "email.verify_mail.subject"
msgstr "Fietsboek Account Verification"
-#: fietsboek/views/account.py:68
+#: fietsboek/views/account.py:75
msgid "email.verify.text"
msgstr ""
"To verify your Fietsboek account, please use this link: {}\n"
"\n"
"If you did not create an account, ignore this email."
-#: fietsboek/views/account.py:72
+#: fietsboek/views/account.py:86
msgid "flash.a_confirmation_link_has_been_sent"
msgstr "A confirmation link has been sent"
-#: fietsboek/views/admin.py:45
+#: fietsboek/views/admin.py:49
msgid "flash.badge_added"
msgstr "Badge has been added"
-#: fietsboek/views/admin.py:69
+#: fietsboek/views/admin.py:73
msgid "flash.badge_modified"
msgstr "Badge has been modified"
-#: fietsboek/views/admin.py:89
+#: fietsboek/views/admin.py:93
msgid "flash.badge_deleted"
msgstr "Badge has been deleted"
-#: fietsboek/views/default.py:75
+#: fietsboek/views/default.py:114
msgid "flash.invalid_credentials"
msgstr "Invalid login credentials"
-#: fietsboek/views/default.py:79
+#: fietsboek/views/default.py:118
msgid "flash.account_not_verified"
msgstr "Your account is not verified yet"
-#: fietsboek/views/default.py:82
+#: fietsboek/views/default.py:121
msgid "flash.logged_in"
msgstr "You are now logged in"
-#: fietsboek/views/default.py:96
+#: fietsboek/views/default.py:143
msgid "flash.logged_out"
msgstr "You have been logged out"
-#: fietsboek/views/default.py:127
+#: fietsboek/views/default.py:177
msgid "flash.reset_invalid_email"
msgstr "Invalid email address provided"
-#: fietsboek/views/default.py:132
+#: fietsboek/views/default.py:182
msgid "flash.password_token_generated"
msgstr "A password reset email has been sent"
-#: fietsboek/views/default.py:137
+#: fietsboek/views/default.py:187
msgid "page.password_reset.email.subject"
msgstr "Fietsboek Password Reset"
-#: fietsboek/views/default.py:141
+#: fietsboek/views/default.py:190
msgid "page.password_reset.email.body"
msgstr ""
"You can reset your Fietsboek password here: {}\n"
"\n"
"If you did not request a password reset, ignore this email."
-#: fietsboek/views/default.py:168
+#: fietsboek/views/default.py:223
msgid "flash.email_verified"
msgstr "Your email address has been verified"
-#: fietsboek/views/default.py:182
+#: fietsboek/views/default.py:237
msgid "flash.password_updated"
msgstr "Password has been updated"
-#: fietsboek/views/detail.py:94
+#: fietsboek/views/detail.py:101
msgid "flash.track_deleted"
msgstr "Track has been deleted"
-#: fietsboek/views/profile.py:57
+#: fietsboek/views/profile.py:61
msgid "flash.personal_data_updated"
msgstr "Personal data has been updated"
-#: fietsboek/views/profile.py:77
+#: fietsboek/views/profile.py:79
msgid "flash.friend_not_found"
msgstr "The friend was not found"
-#: fietsboek/views/profile.py:82
+#: fietsboek/views/profile.py:85
msgid "flash.friend_already_exists"
msgstr "Friend already exists"
-#: fietsboek/views/profile.py:90
+#: fietsboek/views/profile.py:93
msgid "flash.friend_added"
msgstr "Friend has been added"
-#: fietsboek/views/profile.py:100
+#: fietsboek/views/profile.py:103
msgid "flash.friend_request_sent"
msgstr "Friend request sent"
-#: fietsboek/views/upload.py:52
+#: fietsboek/views/upload.py:56
msgid "flash.no_file_selected"
msgstr "No file selected"
-#: fietsboek/views/upload.py:62
+#: fietsboek/views/upload.py:66
msgid "flash.invalid_file"
msgstr "Invalid GPX file selected"
-#: fietsboek/views/upload.py:179
+#: fietsboek/views/upload.py:189
msgid "flash.upload_success"
msgstr "Upload successful"
-#: fietsboek/views/upload.py:195
+#: fietsboek/views/upload.py:205
msgid "flash.upload_cancelled"
msgstr "Upload cancelled"
diff --git a/fietsboek/locale/fietslog.pot b/fietsboek/locale/fietslog.pot
index abb2bfc..a86c43b 100644
--- a/fietsboek/locale/fietslog.pot
+++ b/fietsboek/locale/fietslog.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2022-11-15 23:42+0100\n"
+"POT-Creation-Date: 2022-12-10 17:37+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,39 +17,39 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.11.0\n"
-#: fietsboek/util.py:282
+#: fietsboek/util.py:274
msgid "password_constraint.mismatch"
msgstr ""
-#: fietsboek/util.py:284
+#: fietsboek/util.py:276
msgid "password_constraint.length"
msgstr ""
-#: fietsboek/models/track.py:526
+#: fietsboek/models/track.py:543
msgid "tooltip.table.length"
msgstr ""
-#: fietsboek/models/track.py:527
+#: fietsboek/models/track.py:544
msgid "tooltip.table.uphill"
msgstr ""
-#: fietsboek/models/track.py:528
+#: fietsboek/models/track.py:545
msgid "tooltip.table.downhill"
msgstr ""
-#: fietsboek/models/track.py:529
+#: fietsboek/models/track.py:546
msgid "tooltip.table.moving_time"
msgstr ""
-#: fietsboek/models/track.py:530
+#: fietsboek/models/track.py:547
msgid "tooltip.table.stopped_time"
msgstr ""
-#: fietsboek/models/track.py:531
+#: fietsboek/models/track.py:549
msgid "tooltip.table.max_speed"
msgstr ""
-#: fietsboek/models/track.py:533
+#: fietsboek/models/track.py:553
msgid "tooltip.table.avg_speed"
msgstr ""
@@ -205,43 +205,43 @@ msgstr ""
msgid "page.browse.no_tracks"
msgstr ""
-#: fietsboek/templates/create_account.jinja2:4
+#: fietsboek/templates/create_account.jinja2:5
msgid "page.create_account.title"
msgstr ""
-#: fietsboek/templates/create_account.jinja2:11
+#: fietsboek/templates/create_account.jinja2:13
msgid "page.create_account.email_invalid"
msgstr ""
-#: fietsboek/templates/create_account.jinja2:13
+#: fietsboek/templates/create_account.jinja2:15
msgid "page.create_account.email"
msgstr ""
-#: fietsboek/templates/create_account.jinja2:22
+#: fietsboek/templates/create_account.jinja2:24
msgid "page.create_account.name_invalid"
msgstr ""
-#: fietsboek/templates/create_account.jinja2:24
+#: fietsboek/templates/create_account.jinja2:26
msgid "page.create_account.name"
msgstr ""
-#: fietsboek/templates/create_account.jinja2:33
+#: fietsboek/templates/create_account.jinja2:35
msgid "page.create_account.password_invalid"
msgstr ""
-#: fietsboek/templates/create_account.jinja2:35
+#: fietsboek/templates/create_account.jinja2:37
msgid "page.create_account.password"
msgstr ""
-#: fietsboek/templates/create_account.jinja2:44
+#: fietsboek/templates/create_account.jinja2:46
msgid "page.create_account.password_must_match"
msgstr ""
-#: fietsboek/templates/create_account.jinja2:46
+#: fietsboek/templates/create_account.jinja2:48
msgid "page.create_account.repeat_password"
msgstr ""
-#: fietsboek/templates/create_account.jinja2:52
+#: fietsboek/templates/create_account.jinja2:54
msgid "page.create_account.create"
msgstr ""
@@ -453,47 +453,47 @@ msgstr ""
msgid "page.home.total"
msgstr ""
-#: fietsboek/templates/layout.jinja2:40
+#: fietsboek/templates/layout.jinja2:35
msgid "page.navbar.toggle"
msgstr ""
-#: fietsboek/templates/layout.jinja2:51
+#: fietsboek/templates/layout.jinja2:46
msgid "page.navbar.home"
msgstr ""
-#: fietsboek/templates/layout.jinja2:54
+#: fietsboek/templates/layout.jinja2:49
msgid "page.navbar.browse"
msgstr ""
-#: fietsboek/templates/layout.jinja2:58
+#: fietsboek/templates/layout.jinja2:53
msgid "page.navbar.upload"
msgstr ""
-#: fietsboek/templates/layout.jinja2:67
+#: fietsboek/templates/layout.jinja2:62
msgid "page.navbar.user"
msgstr ""
-#: fietsboek/templates/layout.jinja2:71
+#: fietsboek/templates/layout.jinja2:66
msgid "page.navbar.welcome_user"
msgstr ""
-#: fietsboek/templates/layout.jinja2:74
+#: fietsboek/templates/layout.jinja2:69
msgid "page.navbar.logout"
msgstr ""
-#: fietsboek/templates/layout.jinja2:77
+#: fietsboek/templates/layout.jinja2:72
msgid "page.navbar.profile"
msgstr ""
-#: fietsboek/templates/layout.jinja2:81
+#: fietsboek/templates/layout.jinja2:76
msgid "page.navbar.admin"
msgstr ""
-#: fietsboek/templates/layout.jinja2:87
+#: fietsboek/templates/layout.jinja2:82
msgid "page.navbar.login"
msgstr ""
-#: fietsboek/templates/layout.jinja2:91
+#: fietsboek/templates/layout.jinja2:86
msgid "page.navbar.create_account"
msgstr ""
@@ -509,11 +509,15 @@ msgstr ""
msgid "page.login.password"
msgstr ""
-#: fietsboek/templates/login.jinja2:28
+#: fietsboek/templates/login.jinja2:30
+msgid "page.login.remember-me"
+msgstr ""
+
+#: fietsboek/templates/login.jinja2:38
msgid "page.login.submit"
msgstr ""
-#: fietsboek/templates/login.jinja2:33
+#: fietsboek/templates/login.jinja2:43
msgid "page.login.forgot_password"
msgstr ""
@@ -613,115 +617,115 @@ msgstr ""
msgid "page.upload.form.gpx"
msgstr ""
-#: fietsboek/views/account.py:48
+#: fietsboek/views/account.py:54
msgid "flash.invalid_name"
msgstr ""
-#: fietsboek/views/account.py:53
+#: fietsboek/views/account.py:59
msgid "flash.invalid_email"
msgstr ""
-#: fietsboek/views/account.py:66
+#: fietsboek/views/account.py:72
msgid "email.verify_mail.subject"
msgstr ""
-#: fietsboek/views/account.py:68
+#: fietsboek/views/account.py:75
msgid "email.verify.text"
msgstr ""
-#: fietsboek/views/account.py:72
+#: fietsboek/views/account.py:86
msgid "flash.a_confirmation_link_has_been_sent"
msgstr ""
-#: fietsboek/views/admin.py:45
+#: fietsboek/views/admin.py:49
msgid "flash.badge_added"
msgstr ""
-#: fietsboek/views/admin.py:69
+#: fietsboek/views/admin.py:73
msgid "flash.badge_modified"
msgstr ""
-#: fietsboek/views/admin.py:89
+#: fietsboek/views/admin.py:93
msgid "flash.badge_deleted"
msgstr ""
-#: fietsboek/views/default.py:96
+#: fietsboek/views/default.py:114
msgid "flash.invalid_credentials"
msgstr ""
-#: fietsboek/views/default.py:100
+#: fietsboek/views/default.py:118
msgid "flash.account_not_verified"
msgstr ""
-#: fietsboek/views/default.py:103
+#: fietsboek/views/default.py:121
msgid "flash.logged_in"
msgstr ""
-#: fietsboek/views/default.py:117
+#: fietsboek/views/default.py:143
msgid "flash.logged_out"
msgstr ""
-#: fietsboek/views/default.py:148
+#: fietsboek/views/default.py:177
msgid "flash.reset_invalid_email"
msgstr ""
-#: fietsboek/views/default.py:153
+#: fietsboek/views/default.py:182
msgid "flash.password_token_generated"
msgstr ""
-#: fietsboek/views/default.py:158
+#: fietsboek/views/default.py:187
msgid "page.password_reset.email.subject"
msgstr ""
-#: fietsboek/views/default.py:162
+#: fietsboek/views/default.py:190
msgid "page.password_reset.email.body"
msgstr ""
-#: fietsboek/views/default.py:189
+#: fietsboek/views/default.py:223
msgid "flash.email_verified"
msgstr ""
-#: fietsboek/views/default.py:203
+#: fietsboek/views/default.py:237
msgid "flash.password_updated"
msgstr ""
-#: fietsboek/views/detail.py:94
+#: fietsboek/views/detail.py:101
msgid "flash.track_deleted"
msgstr ""
-#: fietsboek/views/profile.py:57
+#: fietsboek/views/profile.py:61
msgid "flash.personal_data_updated"
msgstr ""
-#: fietsboek/views/profile.py:77
+#: fietsboek/views/profile.py:79
msgid "flash.friend_not_found"
msgstr ""
-#: fietsboek/views/profile.py:82
+#: fietsboek/views/profile.py:85
msgid "flash.friend_already_exists"
msgstr ""
-#: fietsboek/views/profile.py:90
+#: fietsboek/views/profile.py:93
msgid "flash.friend_added"
msgstr ""
-#: fietsboek/views/profile.py:100
+#: fietsboek/views/profile.py:103
msgid "flash.friend_request_sent"
msgstr ""
-#: fietsboek/views/upload.py:52
+#: fietsboek/views/upload.py:56
msgid "flash.no_file_selected"
msgstr ""
-#: fietsboek/views/upload.py:62
+#: fietsboek/views/upload.py:66
msgid "flash.invalid_file"
msgstr ""
-#: fietsboek/views/upload.py:179
+#: fietsboek/views/upload.py:189
msgid "flash.upload_success"
msgstr ""
-#: fietsboek/views/upload.py:195
+#: fietsboek/views/upload.py:205
msgid "flash.upload_cancelled"
msgstr ""
diff --git a/fietsboek/security.py b/fietsboek/security.py
index 84dd88a..6fbc5db 100644
--- a/fietsboek/security.py
+++ b/fietsboek/security.py
@@ -1,8 +1,10 @@
"""Module implementing the user authentication."""
from pyramid.security import Allowed, Denied
-from pyramid.authentication import SessionAuthenticationHelper
+from pyramid.authentication import SessionAuthenticationHelper, AuthTktCookieHelper
from pyramid.authorization import ACLHelper, Everyone, Authenticated
from pyramid.traversal import DefaultRootFactory
+from pyramid.interfaces import ISecurityPolicy
+from zope.interface import implementer
from sqlalchemy import select
@@ -12,16 +14,28 @@ from . import models
ADMIN_PERMISSIONS = {"admin"}
+@implementer(ISecurityPolicy)
class SecurityPolicy:
"""Implementation of the Pyramid security policy."""
- def __init__(self):
+ def __init__(self, cookie_secret):
self.helper = SessionAuthenticationHelper()
+ # The cookie_helper is used for the "Remember me" function, as the
+ # authentication set by the cookie is deliberately kept for a long long
+ # time. The session authentication on the other hand expires after the
+ # set time (by default, 15 minutes).
+ self.cookie_helper = AuthTktCookieHelper(cookie_secret, max_age=2**31 - 1)
def identity(self, request):
"""See :meth:`pyramid.interfaces.ISecurityPolicy.identity`"""
userid = self.helper.authenticated_userid(request)
if userid is None:
+ # Check if there is maybe a "Remember me" cookie
+ auth_info = self.cookie_helper.identify(request) or {}
+ userid = auth_info.get("userid")
+
+ # Still no identity found
+ if userid is None:
return None
query = select(models.User).filter_by(id=int(userid))
@@ -62,6 +76,16 @@ class SecurityPolicy:
"""See :meth:`pyramid.interfaces.ISecurityPolicy.remember`"""
return self.helper.remember(request, userid, **kw)
+ def remember_cookie(self, request, userid, **kw):
+ """Return the headers for remembering the user using the cookie method.
+
+ This is used for the "Remember me" functionality, as the cookie doesn't
+ expire (unlike the session).
+
+ The parameters are the same as for :meth:`remember`.
+ """
+ return self.cookie_helper.remember(request, userid, **kw)
+
def forget(self, request, **kw):
"""See :meth:`pyramid.interfaces.ISecurityPolicy.forget`"""
- return self.helper.forget(request, **kw)
+ return self.helper.forget(request, **kw) + self.cookie_helper.forget(request, **kw)
diff --git a/fietsboek/templates/login.jinja2 b/fietsboek/templates/login.jinja2
index 86e9adb..7a9bf07 100644
--- a/fietsboek/templates/login.jinja2
+++ b/fietsboek/templates/login.jinja2
@@ -22,6 +22,16 @@
</div>
</div>
</div>
+ <div class="row justify-content-center">
+ <div class="col-lg-5 mb-3">
+ <div class="form-check">
+ <input class="form-check-input" type="checkbox" name="remember-me" id="rememberMe" value="on">
+ <label class="form-check-label" for="rememberMe">
+ {{ _("page.login.remember-me") }}
+ </label>
+ </div>
+ </div>
+ </div>
{{ util.hidden_csrf_input() }}
<div class="row justify-content-center">
<div class="col-auto mb-3">
diff --git a/fietsboek/views/default.py b/fietsboek/views/default.py
index 883d8d7..fe9df08 100644
--- a/fietsboek/views/default.py
+++ b/fietsboek/views/default.py
@@ -3,6 +3,7 @@ from pyramid.view import view_config
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from pyramid.security import remember, forget
from pyramid.i18n import TranslationString as _
+from pyramid.interfaces import ISecurityPolicy
from pyramid.renderers import render_to_response
from sqlalchemy import select
@@ -119,7 +120,15 @@ def do_login(request):
request.session.flash(request.localizer.translate(_("flash.logged_in")))
headers = remember(request, str(user.id))
- return HTTPFound("/", headers=headers)
+
+ if request.params.get("remember-me") == "on":
+ # We don't want this logic to be the default in
+ # SecurityPolicy.remember, so we manually fake it here:
+ policy = request.registry.getUtility(ISecurityPolicy)
+ headers += policy.remember_cookie(request, str(user.id))
+
+ response = HTTPFound("/", headers=headers)
+ return response
@view_config(route_name="logout")