diff --git a/app/bird/migrations/0001_initial.py b/app/bird/migrations/0001_initial.py index c9d02e0..74e8ba9 100644 --- a/app/bird/migrations/0001_initial.py +++ b/app/bird/migrations/0001_initial.py @@ -1,6 +1,6 @@ # Generated by Django 4.2.6 on 2023-10-22 09:59 -import ckeditor.fields +import django_ckeditor_5.fields from django.conf import settings from django.db import migrations, models import django.db.models.deletion @@ -22,7 +22,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(primary_key=True, serialize=False)), ('name', models.CharField(max_length=256, unique=True, verbose_name='Bezeichnung')), - ('description', ckeditor.fields.RichTextField(verbose_name='Erläuterungen')), + ('description', django_ckeditor_5.fields.CKEditor5Field(verbose_name='Erläuterungen')), ], options={ 'verbose_name': 'Vogel', diff --git a/app/bird/migrations/0003_expand_bird_model.py b/app/bird/migrations/0003_expand_bird_model.py index cb61734..456ba6f 100644 --- a/app/bird/migrations/0003_expand_bird_model.py +++ b/app/bird/migrations/0003_expand_bird_model.py @@ -1,6 +1,6 @@ # Generated by Django 5.2.2 on 2025-06-07 13:33 -import ckeditor.fields +import django_ckeditor_5.fields import django.db.models.deletion from django.conf import settings from django.db import migrations, models @@ -103,6 +103,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='bird', name='description', - field=ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Erläuterungen'), + field=django_ckeditor_5.fields.CKEditor5Field(blank=True, null=True, verbose_name='Erläuterungen'), ), ] diff --git a/app/bird/migrations/0007_alter_fallenbird_status.py b/app/bird/migrations/0007_alter_fallenbird_status.py new file mode 100644 index 0000000..8ebfdd4 --- /dev/null +++ b/app/bird/migrations/0007_alter_fallenbird_status.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.2 on 2025-06-07 18:05 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('bird', '0006_alter_fallenbird_options_alter_fallenbird_age_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='fallenbird', + name='status', + field=models.ForeignKey(blank=True, default=1, null=True, on_delete=django.db.models.deletion.CASCADE, to='bird.birdstatus', verbose_name='Status'), + ), + ] diff --git a/app/bird/models.py b/app/bird/models.py index bb9459c..3ab0c19 100644 --- a/app/bird/models.py +++ b/app/bird/models.py @@ -5,7 +5,7 @@ from django.conf import settings from django.db import models from django.utils.translation import gettext_lazy as _ -from ckeditor.fields import RichTextField +from django_ckeditor_5.fields import CKEditor5Field from aviary.models import Aviary @@ -125,7 +125,7 @@ class Bird(models.Model): name = models.CharField( max_length=256, unique=True, verbose_name=_("Bezeichnung") ) - description = RichTextField(verbose_name=_("Erläuterungen"), blank=True, null=True) + description = CKEditor5Field(verbose_name=_("Erläuterungen"), blank=True, null=True) species = models.CharField( max_length=256, blank=True, null=True, verbose_name=_("Art") ) diff --git a/app/core/allauth.py b/app/core/allauth.py index fdfa05a..f163f30 100644 --- a/app/core/allauth.py +++ b/app/core/allauth.py @@ -6,11 +6,13 @@ SITE_ID = 1 -ACCOUNT_AUTHENTICATION_METHOD = "username_email" -ACCOUNT_EMAIL_REQUIRED = True +# Updated settings to replace deprecated options +ACCOUNT_LOGIN_METHODS = {"username", "email"} # Replaces ACCOUNT_AUTHENTICATION_METHOD +ACCOUNT_SIGNUP_FIELDS = ["email*", "username*", "password1*", "password2*"] # Replaces ACCOUNT_EMAIL_REQUIRED ACCOUNT_EMAIL_VERIFICATION = "mandatory" -ACCOUNT_LOGIN_ATTEMPTS_LIMIT = 5 -ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT = 900 # 15 Minutes +ACCOUNT_RATE_LIMITS = { + "login_failed": "5/15m", # Replaces ACCOUNT_LOGIN_ATTEMPTS_LIMIT/TIMEOUT (5 attempts per 15 minutes) +} ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True ACCOUNT_LOGOUT_REDIRECT_URL = "/" ACCOUNT_LOGOUT_ON_GET = True diff --git a/app/core/ckeditor.py b/app/core/ckeditor.py index 7a6d3b0..3c97350 100644 --- a/app/core/ckeditor.py +++ b/app/core/ckeditor.py @@ -1,15 +1,90 @@ # ----------------------------------- -# CKEDITOR CONFIGURATION +# CKEDITOR 5 CONFIGURATION # ----------------------------------- -CKEDITOR_BASEPATH = "/static/ckeditor/ckeditor/" -CKEDITOR_UPLOAD_PATH = "media" +customColorPalette = [ + { + 'color': 'hsl(4, 90%, 58%)', + 'label': 'Red' + }, + { + 'color': 'hsl(340, 82%, 52%)', + 'label': 'Pink' + }, + { + 'color': 'hsl(291, 64%, 42%)', + 'label': 'Purple' + }, + { + 'color': 'hsl(262, 52%, 47%)', + 'label': 'Deep Purple' + }, + { + 'color': 'hsl(231, 48%, 48%)', + 'label': 'Indigo' + }, + { + 'color': 'hsl(207, 90%, 54%)', + 'label': 'Blue' + }, +] -CKEDITOR_CONFIGS = { - "default": { - "removePlugins": "exportpdf", - "height": 300, - "width": "100%", - "allowedContent": True, +CKEDITOR_5_CONFIGS = { + 'default': { + 'toolbar': ['heading', '|', 'bold', 'italic', 'link', + 'bulletedList', 'numberedList', 'blockQuote', 'imageUpload', ], + + }, + 'extends': { + 'blockToolbar': [ + 'paragraph', 'heading1', 'heading2', 'heading3', + '|', + 'bulletedList', 'numberedList', + '|', + 'blockQuote', + ], + 'toolbar': ['heading', '|', 'outdent', 'indent', '|', 'bold', 'italic', 'link', 'underline', 'strikethrough', + 'code','subscript', 'superscript', 'highlight', '|', 'codeBlock', 'sourceEditing', 'insertImage', + 'bulletedList', 'numberedList', 'todoList', '|', 'blockQuote', 'imageUpload', '|', + 'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor', 'mediaEmbed', 'removeFormat', + 'insertTable',], + 'image': { + 'toolbar': ['imageTextAlternative', '|', 'imageStyle:alignLeft', + 'imageStyle:alignRight', 'imageStyle:alignCenter', 'imageStyle:side', ], + 'styles': [ + 'full', + 'side', + 'alignLeft', + 'alignRight', + 'alignCenter', + ] + }, + 'table': { + 'contentToolbar': [ 'tableColumn', 'tableRow', 'mergeTableCells', + 'tableProperties', 'tableCellProperties' ], + 'tableProperties': { + 'borderColors': customColorPalette, + 'backgroundColors': customColorPalette + }, + 'tableCellProperties': { + 'borderColors': customColorPalette, + 'backgroundColors': customColorPalette + } + }, + 'heading' : { + 'options': [ + { 'model': 'paragraph', 'title': 'Paragraph', 'class': 'ck-heading_paragraph' }, + { 'model': 'heading1', 'view': 'h1', 'title': 'Heading 1', 'class': 'ck-heading_heading1' }, + { 'model': 'heading2', 'view': 'h2', 'title': 'Heading 2', 'class': 'ck-heading_heading2' }, + { 'model': 'heading3', 'view': 'h3', 'title': 'Heading 3', 'class': 'ck-heading_heading3' } + ] + } + }, + 'list': { + 'properties': { + 'styles': 'true', + 'startIndex': 'true', + 'reversed': 'true', + } } } diff --git a/app/core/csp.py b/app/core/csp.py index 04963eb..bf80532 100644 --- a/app/core/csp.py +++ b/app/core/csp.py @@ -5,7 +5,7 @@ CSP_DEFAULT_SRC = ( "'self'", "https://cdn.datatables.net", - "https://cke4.ckeditor.com", + "https://cdn.ckeditor.com", ) CSP_STYLE_SRC = ( "'self'", diff --git a/app/core/settings.py b/app/core/settings.py index 3c818a3..3a45a7d 100644 --- a/app/core/settings.py +++ b/app/core/settings.py @@ -74,10 +74,9 @@ INSTALLED_APPS = [ "crispy_bootstrap5", "crispy_forms", # ----------------------------------- - # CKEditor + # CKEditor 5 # ----------------------------------- - "ckeditor", - "ckeditor_uploader", + "django_ckeditor_5", # ----------------------------------- # My Apps # ----------------------------------- @@ -209,11 +208,10 @@ CRISPY_TEMPLATE_PACK = "bootstrap5" # ----------------------------------- try: from .allauth import ( - ACCOUNT_AUTHENTICATION_METHOD, - ACCOUNT_EMAIL_REQUIRED, + ACCOUNT_LOGIN_METHODS, + ACCOUNT_SIGNUP_FIELDS, ACCOUNT_EMAIL_VERIFICATION, - ACCOUNT_LOGIN_ATTEMPTS_LIMIT, - ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT, + ACCOUNT_RATE_LIMITS, ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION, ACCOUNT_LOGOUT_REDIRECT_URL, ACCOUNT_LOGOUT_ON_GET, @@ -262,8 +260,8 @@ try: except ImportError: print("No Jazzmin Settings found!") -# CKEditor +# CKEditor 5 try: - from .ckeditor import CKEDITOR_CONFIGS, CKEDITOR_BASEPATH, CKEDITOR_UPLOAD_PATH + from .ckeditor import CKEDITOR_5_CONFIGS except ImportError: print("No CKEditor Settings found!") diff --git a/app/requirements.txt b/app/requirements.txt index 34d60c7..0f1d504 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -2,7 +2,7 @@ crispy-bootstrap5>=0.6 django-allauth>=0.55 django-bootstrap-datepicker-plus>=4.0 django-bootstrap-modal-forms>=2 -django-ckeditor>=6.6 +django-ckeditor-5>=0.2 django-crispy-forms>=1 django-csp>=3.7 django-environ>=0.9 diff --git a/test/test_settings.py b/test/test_settings.py index cce207d..29bada9 100644 --- a/test/test_settings.py +++ b/test/test_settings.py @@ -39,8 +39,7 @@ THIRD_PARTY_APPS = [ 'allauth.account', 'bootstrap_datepicker_plus', 'bootstrap_modal_forms', - 'ckeditor', - 'ckeditor_uploader', + 'django_ckeditor_5', ] # Local apps @@ -151,11 +150,10 @@ LOGOUT_REDIRECT_URL = '/' CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5' CRISPY_TEMPLATE_PACK = 'bootstrap5' -# CKEditor settings for tests -CKEDITOR_UPLOAD_PATH = tempfile.mkdtemp() -CKEDITOR_CONFIGS = { +# CKEditor 5 settings for tests +CKEDITOR_5_CONFIGS = { 'default': { - 'toolbar': 'Basic', + 'toolbar': ['bold', 'italic', 'underline', '|', 'bulletedList', 'numberedList'], }, } diff --git a/update_checklist.md b/update_checklist.md index f7888e5..e2edf2c 100644 --- a/update_checklist.md +++ b/update_checklist.md @@ -11,14 +11,17 @@ Dieses Dokument listet alle Abhängigkeiten auf, die Updates benötigen, sowie S ## 🚨 Kritische Sicherheitsupdates -### 1. CKEditor (HOCH PRIORITÄT) -- **Aktuell:** django-ckeditor 6.7.3 (bündelt CKEditor 4.22.1) -- **Problem:** CKEditor 4.22.1 wird nicht mehr unterstützt und hat bekannte Sicherheitslücken -- **Empfehlung:** Migration zu CKEditor 5 oder einem alternativen Editor -- **Alternativen:** - - `django-ckeditor-5` (falls CKEditor 5 Lizenz akzeptabel) - - `django-tinymce` (Alternative Rich Text Editor) - - `django-froala-editor` (kommerzielle Alternative) +### 1. CKEditor (HOCH PRIORITÄT) ✅ ABGESCHLOSSEN +- **Früher:** django-ckeditor 6.7.3 (bündelte CKEditor 4.22.1) +- **Problem:** CKEditor 4.22.1 war nicht mehr unterstützt und hatte bekannte Sicherheitslücken +- **Lösung:** ✅ Migration zu CKEditor 5 abgeschlossen +- **Implementiert:** + - ✅ `django-ckeditor-5==0.2.18` installiert + - ✅ Alle Django Settings auf CKEditor 5 umgestellt + - ✅ CSP Settings für CKEditor 5 CDN aktualisiert + - ✅ Migration Files korrigiert und Datenbank migriert + - ✅ Alle Tests erfolgreich (keine Deprecated Warnings) + - ✅ Web-Interface funktioniert korrekt mit CKEditor 5 ### 2. Django-allauth Settings (MITTEL PRIORITÄT) - **Problem:** Veraltete Settings-Optionen werden verwendet @@ -99,19 +102,16 @@ Dieses Dokument listet alle Abhängigkeiten auf, die Updates benötigen, sowie S ## 🛠️ Empfohlene Update-Reihenfolge -### Phase 1: Kritische Sicherheitsupdates (Sofort) -1. **CKEditor ersetzen** - ```bash - # Option 1: django-ckeditor-5 verwenden - pip uninstall django-ckeditor - pip install django-ckeditor-5 - - # Option 2: TinyMCE verwenden - pip uninstall django-ckeditor - pip install django-tinymce - ``` +### Phase 1: Kritische Sicherheitsupdates ✅ ABGESCHLOSSEN +1. **CKEditor Migration** ✅ **ABGESCHLOSSEN** + - ✅ django-ckeditor-5==0.2.18 installiert + - ✅ Django Settings komplett umgestellt + - ✅ Migration Files korrigiert + - ✅ Datenbank erfolgreich migriert + - ✅ CSP Security Policy aktualisiert + - ✅ Web-Interface getestet und funktionsfähig -2. **Django-allauth Settings aktualisieren** +2. **Django-allauth Settings aktualisieren** 🟡 **NOCH OFFEN** - Settings in `core/settings.py` anpassen - Deprecated Warnings beheben