diff options
-rw-r--r-- | fietsboek/models/user.py | 5 | ||||
-rw-r--r-- | fietsboek/static/fietsboek.js | 21 | ||||
-rw-r--r-- | fietsboek/static/theme.css | 4 | ||||
-rw-r--r-- | fietsboek/templates/home.jinja2 | 8 | ||||
-rw-r--r-- | fietsboek/util.py | 16 | ||||
-rw-r--r-- | fietsboek/views/detail.py | 2 | ||||
-rw-r--r-- | fietsboek/views/upload.py | 2 |
7 files changed, 42 insertions, 16 deletions
diff --git a/fietsboek/models/user.py b/fietsboek/models/user.py index fa6bd7f..83a36e0 100644 --- a/fietsboek/models/user.py +++ b/fietsboek/models/user.py @@ -2,7 +2,7 @@ import datetime import enum import uuid -import os +import secrets from sqlalchemy import ( Column, @@ -45,6 +45,7 @@ SCRYPT_PARAMETERS = { 'r': 8, 'p': 1, } +SALT_LENGTH = 32 friends_assoc = Table( @@ -130,7 +131,7 @@ class User(Base): :type new_password: str """ new_password = new_password.encode('utf-8') - salt = os.urandom(16) + salt = secrets.token_bytes(SALT_LENGTH) scrypt = Scrypt(salt=salt, **SCRYPT_PARAMETERS) password = scrypt.derive(new_password) self.salt = salt diff --git a/fietsboek/static/fietsboek.js b/fietsboek/static/fietsboek.js index bf402a8..2e88aa3 100644 --- a/fietsboek/static/fietsboek.js +++ b/fietsboek/static/fietsboek.js @@ -271,6 +271,27 @@ function saveImageDescriptionClicked(event) { addHandler("#imageDescriptionModal button.btn-success", "click", saveImageDescriptionClicked); +/** + * Handler to toggle (collapse/expand) the yearly/monthly summary. + * + * @param event - The triggering event. + */ +function toggleSummary(event) { + const chevron = event.target; + const containing = chevron.closest("a"); + const summary = containing.nextElementSibling; + bootstrap.Collapse.getOrCreateInstance(summary).toggle(); + if (chevron.classList.contains("bi-chevron-down")) { + chevron.classList.remove("bi-chevron-down"); + chevron.classList.add("bi-chevron-right"); + } else { + chevron.classList.remove("bi-chevron-right"); + chevron.classList.add("bi-chevron-down"); + } +} + +addHandler(".summary-toggler", "click", toggleSummary); + /* * Handler to enable the "Download archive button" ... */ diff --git a/fietsboek/static/theme.css b/fietsboek/static/theme.css index 01ee06e..3f7f74d 100644 --- a/fietsboek/static/theme.css +++ b/fietsboek/static/theme.css @@ -81,6 +81,10 @@ strong { max-width: 100%; } +.summary-toggler { + cursor: pointer; +} + .admin-badge-list * { margin-left: 5px; margin-right: 5px; diff --git a/fietsboek/templates/home.jinja2 b/fietsboek/templates/home.jinja2 index 6825c62..4a9c3ad 100644 --- a/fietsboek/templates/home.jinja2 +++ b/fietsboek/templates/home.jinja2 @@ -6,11 +6,11 @@ {% if summary %} <div class="list-group list-group-root"> {% for year in summary %} - <a class="list-group-item list-group-item-primary">{{ year.year }} — {{ (year.total_length / 1000) | round(2) | format_decimal }} km</a> - <div class="list-group"> + <a class="list-group-item list-group-item-primary"><i class="bi bi-chevron-down summary-toggler"></i> {{ year.year }} — {{ (year.total_length / 1000) | round(2) | format_decimal }} km</a> + <div class="list-group collapse show"> {% for month in year %} - <a class="list-group-item list-group-item-secondary">{{ month_name(request, month.month) }} — {{ (month.total_length / 1000) | round(2) | format_decimal }} km</a> - <div class="list-group"> + <a class="list-group-item list-group-item-secondary"><i class="bi bi-chevron-down summary-toggler"></i> {{ month_name(request, month.month) }} — {{ (month.total_length / 1000) | round(2) | format_decimal }} km</a> + <div class="list-group collapse show"> {% for track in month %} <span class="list-group-item">{{ track.date.day }}: <a href="{{ request.route_url('details', track_id=track.id) }}" data-bs-toggle="tooltip" data-bs-container="body" data-bs-html="true" title="{{ track.html_tooltip(request.localizer) }}">{{ track.title | default(track.date, true) }}</a></span> {% endfor %} diff --git a/fietsboek/util.py b/fietsboek/util.py index ddc4832..71f5d16 100644 --- a/fietsboek/util.py +++ b/fietsboek/util.py @@ -1,10 +1,9 @@ """Various utility functions.""" -import random -import string import datetime import re import os import unicodedata +import secrets # Compat for Python < 3.9 import importlib_resources @@ -213,16 +212,17 @@ def month_name(request, month): return locale.months["stand-alone"]["wide"][month] -def random_alphanum_string(length=20): - """Draws a random string that consists of alphanumerical characters. +def random_link_secret(nbytes=20): + """Safely generates a secret suitable for the link share. - :param length: Length of the string. - :type length: int + The returned string consists of characters that are safe to use in a URL. + + :param nbytes: Number of random bytes to use. + :type nbytes: int :return: A randomly drawn string. :rtype: str """ - candidates = string.ascii_letters + string.digits - return ''.join(random.choice(candidates) for _ in range(length)) + return secrets.token_urlsafe(nbytes) def retrieve_multiple(dbsession, model, params, name): diff --git a/fietsboek/views/detail.py b/fietsboek/views/detail.py index f213231..6eba3ce 100644 --- a/fietsboek/views/detail.py +++ b/fietsboek/views/detail.py @@ -73,7 +73,7 @@ def invalidate_share(request): :rtype: pyramid.response.Response """ track = request.context - track.link_secret = util.random_alphanum_string() + track.link_secret = util.random_link_secret() return HTTPFound(request.route_url('details', track_id=track.id)) diff --git a/fietsboek/views/upload.py b/fietsboek/views/upload.py index ea05c74..95008b4 100644 --- a/fietsboek/views/upload.py +++ b/fietsboek/views/upload.py @@ -155,7 +155,7 @@ def do_finish_upload(request): visibility=Visibility[request.params["visibility"]], description=request.params["description"], badges=badges, - link_secret=util.random_alphanum_string(), + link_secret=util.random_link_secret(), tagged_people=tagged_people, ) track.date = date |