mirror of
https://gitlab.com/psono/psono-server
synced 2025-04-18 09:44:01 +03:00
Merge branch 'develop' into 'master'
Preparing v5.0.14 See merge request psono/psono-server!231
This commit is contained in:
commit
f9e4b8e354
@ -301,8 +301,8 @@ build-sbom:
|
||||
- echo $CI_JOB_TOKEN | docker login --username=gitlab-ci-token --password-stdin registry.gitlab.com
|
||||
- docker pull $CONTAINER_TEST_IMAGE
|
||||
- curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
|
||||
- syft scan psono/psono-combo-enterprise:latest -o cyclonedx-json > sbom.json
|
||||
- mv /builds/psono/psono-server/sbom.json ../
|
||||
- syft scan $CONTAINER_TEST_IMAGE -o cyclonedx-json > sbom.json
|
||||
- mv ./sbom.json ../
|
||||
- rm -Rf *
|
||||
- rm -Rf .* 2> /dev/null || true
|
||||
- mv ../sbom.json ./
|
||||
|
@ -182,6 +182,9 @@ if isinstance(ALLOWED_SECOND_FACTORS, str) and ALLOWED_SECOND_FACTORS:
|
||||
elif isinstance(ALLOWED_SECOND_FACTORS, str):
|
||||
ALLOWED_SECOND_FACTORS = []
|
||||
|
||||
|
||||
FAVICON_SERVICE_URL = config_get('FAVICON_SERVICE_URL', 'https://favicon.psono.com/v1/icon/')
|
||||
|
||||
ALLOW_USER_SEARCH_BY_EMAIL = str(config_get('ALLOW_USER_SEARCH_BY_EMAIL', False)).lower() == 'true'
|
||||
ALLOW_USER_SEARCH_BY_USERNAME_PARTIAL = str(config_get('ALLOW_USER_SEARCH_BY_USERNAME_PARTIAL', False)).lower() == 'true'
|
||||
|
||||
@ -708,6 +711,7 @@ def generate_signature():
|
||||
'management': MANAGEMENT_ENABLED,
|
||||
'files': FILES_ENABLED,
|
||||
'allowed_file_repository_types': ALLOWED_FILE_REPOSITORY_TYPES,
|
||||
'favicon_service_url': FAVICON_SERVICE_URL,
|
||||
'auto_prolongation_token_time_valid': AUTO_PROLONGATION_TOKEN_TIME_VALID,
|
||||
'allowed_second_factors': ALLOWED_SECOND_FACTORS,
|
||||
'disable_central_security_reports': DISABLE_CENTRAL_SECURITY_REPORTS,
|
||||
|
19
psono/restapi/migrations/0040_user_zoneinfo.py
Normal file
19
psono/restapi/migrations/0040_user_zoneinfo.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.20 on 2025-04-16 10:20
|
||||
|
||||
from django.db import migrations
|
||||
import timezone_field.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('restapi', '0039_link_share_allow_write_alter_ivalt_mobile_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='zoneinfo',
|
||||
field=timezone_field.fields.TimeZoneField(null=True),
|
||||
),
|
||||
]
|
@ -4,6 +4,7 @@ from django.dispatch import receiver
|
||||
from django.core.cache import cache
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from timezone_field import TimeZoneField
|
||||
|
||||
from decimal import Decimal
|
||||
import binascii
|
||||
@ -76,6 +77,7 @@ class User(models.Model):
|
||||
last_login = models.DateTimeField(default=timezone.now)
|
||||
hashing_algorithm = models.CharField('hashing algorithm', max_length=32, default=DEFAULT_HASHING_ALGORITHM, choices=HASHING_ALGORITHMS,)
|
||||
hashing_parameters = models.JSONField('hashing parameters', default=default_hashing_parameters)
|
||||
zoneinfo = TimeZoneField(null=True)
|
||||
|
||||
credit = models.DecimalField(max_digits=24, decimal_places=16, default=Decimal(str(0)))
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
from django.utils.crypto import constant_time_compare
|
||||
|
||||
from timezone_field.backends import TimeZoneNotFoundError, get_tz_backend
|
||||
from rest_framework import serializers, exceptions
|
||||
import nacl.utils
|
||||
from nacl.exceptions import CryptoError
|
||||
@ -9,8 +9,10 @@ import nacl.encoding
|
||||
class ActivateTokenSerializer(serializers.Serializer):
|
||||
verification = serializers.CharField(required=True)
|
||||
verification_nonce = serializers.CharField(max_length=64, required=True)
|
||||
zoneinfo = serializers.CharField(required=False, default=None, allow_null=True)
|
||||
|
||||
def validate(self, attrs: dict) -> dict:
|
||||
zoneinfo = attrs.get('zoneinfo', None)
|
||||
verification_hex = attrs.get('verification', '')
|
||||
verification = nacl.encoding.HexEncoder.decode(verification_hex)
|
||||
verification_nonce_hex = attrs.get('verification_nonce', '')
|
||||
@ -55,5 +57,12 @@ class ActivateTokenSerializer(serializers.Serializer):
|
||||
msg = 'VERIFICATION_CODE_INCORRECT'
|
||||
raise exceptions.ValidationError(msg)
|
||||
|
||||
tz_backend = get_tz_backend(use_pytz=None)
|
||||
try:
|
||||
zoneinfo = tz_backend.to_tzobj(zoneinfo)
|
||||
except TimeZoneNotFoundError:
|
||||
zoneinfo = None
|
||||
|
||||
attrs['zoneinfo'] = zoneinfo
|
||||
attrs['token'] = token
|
||||
return attrs
|
@ -1,6 +1,7 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.hashers import check_password
|
||||
from rest_framework import serializers, exceptions
|
||||
from timezone_field.backends import TimeZoneNotFoundError, get_tz_backend
|
||||
|
||||
import re
|
||||
import bcrypt
|
||||
@ -28,8 +29,10 @@ class UserUpdateSerializer(serializers.Serializer):
|
||||
hashing_parameters = serializers.DictField(required=False, )
|
||||
|
||||
language = serializers.CharField(max_length=16, required=False, allow_null=True)
|
||||
zoneinfo = serializers.CharField(required=False, default=None, allow_null=True)
|
||||
|
||||
def validate(self, attrs: dict) -> dict:
|
||||
zoneinfo = attrs.get('zoneinfo', None)
|
||||
email = attrs.get('email')
|
||||
authkey_old = attrs.get('authkey_old')
|
||||
authkey = attrs.get('authkey', False)
|
||||
@ -98,6 +101,14 @@ class UserUpdateSerializer(serializers.Serializer):
|
||||
msg = 'INVALID_HASHING_PARAMETER'
|
||||
raise exceptions.ValidationError(msg)
|
||||
|
||||
tz_backend = get_tz_backend(use_pytz=None)
|
||||
try:
|
||||
zoneinfo = tz_backend.to_tzobj(zoneinfo)
|
||||
except TimeZoneNotFoundError:
|
||||
zoneinfo = None
|
||||
|
||||
attrs['zoneinfo'] = zoneinfo
|
||||
|
||||
attrs['hashing_algorithm'] = hashing_algorithm
|
||||
attrs['hashing_parameters'] = hashing_parameters
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
from anymail.exceptions import AnymailUnsupportedFeature
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.generics import GenericAPIView
|
||||
@ -7,6 +6,7 @@ from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import translation
|
||||
from django.utils.formats import date_format
|
||||
|
||||
import os
|
||||
from email.mime.image import MIMEImage
|
||||
@ -54,6 +54,7 @@ class ActivateTokenView(GenericAPIView):
|
||||
)
|
||||
|
||||
token = serializer.validated_data['token']
|
||||
zoneinfo = serializer.validated_data['zoneinfo']
|
||||
|
||||
token.active = True
|
||||
token.user_validator = None
|
||||
@ -62,6 +63,8 @@ class ActivateTokenView(GenericAPIView):
|
||||
ip_address = get_ip(request)
|
||||
login_datetime = timezone.now()
|
||||
|
||||
if not request.user.zoneinfo and zoneinfo:
|
||||
request.user.zoneinfo = zoneinfo
|
||||
request.user.last_login = login_datetime
|
||||
request.user.save()
|
||||
|
||||
@ -74,8 +77,16 @@ class ActivateTokenView(GenericAPIView):
|
||||
email = decrypt_with_db_secret(request.user.email)
|
||||
|
||||
with translation.override(request.LANGUAGE_CODE):
|
||||
|
||||
if request.user.zoneinfo:
|
||||
login_datetime_timezone = login_datetime.astimezone(request.user.zoneinfo)
|
||||
login_datetime_timezone_str = date_format(login_datetime_timezone, format='DATETIME_FORMAT') + ' ' + login_datetime_timezone.tzname()
|
||||
else:
|
||||
login_datetime_timezone_str = date_format(login_datetime, format='DATETIME_FORMAT') + ' UTC'
|
||||
|
||||
subject = render_to_string('email/new_login_subject.txt', {
|
||||
'ip_address': ip_address,
|
||||
'login_datetime_timezone': login_datetime_timezone_str,
|
||||
'login_datetime': login_datetime,
|
||||
'email': email,
|
||||
'username': request.user.username,
|
||||
@ -85,6 +96,7 @@ class ActivateTokenView(GenericAPIView):
|
||||
}).replace('\n', ' ').replace('\r', '')
|
||||
msg_plain = render_to_string('email/new_login.txt', {
|
||||
'ip_address': ip_address,
|
||||
'login_datetime_timezone': login_datetime_timezone_str,
|
||||
'login_datetime': login_datetime,
|
||||
'email': email,
|
||||
'username': request.user.username,
|
||||
@ -94,6 +106,7 @@ class ActivateTokenView(GenericAPIView):
|
||||
})
|
||||
msg_html = render_to_string('email/new_login.html', {
|
||||
'ip_address': ip_address,
|
||||
'login_datetime_timezone': login_datetime_timezone_str,
|
||||
'login_datetime': login_datetime,
|
||||
'email': email,
|
||||
'username': request.user.username,
|
||||
|
@ -62,6 +62,9 @@ class UserUpdate(GenericAPIView):
|
||||
if 'language' in request.data and request.data['language'] is not None:
|
||||
request.user.language = str(request.data['language'])
|
||||
|
||||
if serializer.validated_data['zoneinfo'] is not None:
|
||||
request.user.zoneinfo = serializer.validated_data['zoneinfo']
|
||||
|
||||
# Password Change
|
||||
if 'authkey' in request.data and request.data['authkey'] is not None:
|
||||
request.user.authkey = make_password(str(request.data['authkey']))
|
||||
|
@ -98,7 +98,7 @@
|
||||
<p style="font-family:sans-serif;font-size:14px;font-weight:normal;margin: 0 0 15px;">
|
||||
{% trans "We noticed a new login to your Psono account:" %}</p>
|
||||
<p style="font-family:sans-serif;font-size:14px;font-weight:normal;margin: 0 0 15px;">
|
||||
<b>{% trans "Date & Time:" %}</b> {{ login_datetime|localize }}</p>
|
||||
<b>{% trans "Date & Time:" %}</b> {{ login_datetime_timezone }}</p>
|
||||
<p style="font-family:sans-serif;font-size:14px;font-weight:normal;margin: 0 0 15px;">
|
||||
<b>{% trans "IP Address:" %}</b> {{ ip_address }}</p>
|
||||
<p style="font-family:sans-serif;font-size:14px;font-weight:normal;margin: 0 0 15px;">
|
||||
|
@ -3,7 +3,7 @@
|
||||
{% trans "Hello," %}
|
||||
{% trans "We noticed a new login to your Psono account:" %}
|
||||
|
||||
{% trans "Date & Time:" %} {{ login_datetime|localize }}
|
||||
{% trans "Date & Time:" %} {{ login_datetime_timezone }}
|
||||
{% trans "IP Address:" %} {{ ip_address }}
|
||||
{% trans "Username:" %} {{ username }}
|
||||
|
||||
|
@ -32,4 +32,5 @@ dj-database-url==2.1.0
|
||||
toml==0.10.2
|
||||
azure-storage-blob==12.13.0
|
||||
webauthn==2.5.2
|
||||
tzdata==2023.4
|
||||
tzdata==2023.4
|
||||
django-timezone-field==7.1.0
|
||||
|
@ -84,6 +84,7 @@ django==4.2.20
|
||||
# django-filter
|
||||
# django-redis
|
||||
# django-storages
|
||||
# django-timezone-field
|
||||
# djangorestframework
|
||||
# sentry-sdk
|
||||
django-anymail==12.0
|
||||
@ -96,6 +97,8 @@ django-redis==5.4.0
|
||||
# via -r requirements.in
|
||||
django-storages[azure,boto3,google,libcloud,sftp]==1.14.2
|
||||
# via -r requirements.in
|
||||
django-timezone-field==7.1
|
||||
# via -r requirements.in
|
||||
djangorestframework==3.15.2
|
||||
# via -r requirements.in
|
||||
duo-client==4.2.3
|
||||
|
Loading…
x
Reference in New Issue
Block a user