diff --git a/.env.sample b/.env.sample new file mode 100644 index 00000000..9398e5a0 --- /dev/null +++ b/.env.sample @@ -0,0 +1,42 @@ +# General +SECRET_KEY=CHANGE_ME +DEBUG=True +ALLOWED_HOSTS=127.0.0.1,localhost,example.org +BASE_URL=http://localhost:8002 +ADMINS=Admin1:mail@example.org,Admin2:mail2@example.org + +# Database +DB_USER=postgres +DB_PASSWORD= +DB_NAME=konova +DB_HOST=127.0.0.1 +DB_PORT=5432 + +# E-Mail +SMTP_HOST=localhost +SMTP_PORT=25 +REPLY_TO_ADDR=ksp-servicestelle@sgdnord.rlp.de +DEFAULT_FROM_EMAIL=service@ksp.de + +# Proxy +PROXY=CHANGE_ME +GEOPORTAL_RLP_USER=CHANGE_ME +GEOPORTAL_RLP_PASSWORD=CHANGE_ME + +# Schneider +SCHNEIDER_BASE_URL=https://schneider.naturschutz.rlp.de +SCHNEIDER_AUTH_TOKEN=CHANGE_ME +SCHNEIDER_AUTH_HEADER=auth + +# SSO +SSO_SERVER_BASE_URL=https://login.naturschutz.rlp.de +OAUTH_CODE_VERIFIER=CHANGE_ME +OAUTH_CLIENT_ID=CHANGE_ME +OAUTH_CLIENT_SECRET=CHANGE_ME + +# RabbitMQ +## For connections to EGON +EGON_RABBITMQ_HOST=CHANGE_ME +EGON_RABBITMQ_PORT=CHANGE_ME +EGON_RABBITMQ_USER=CHANGE_ME +EGON_RABBITMQ_PW=CHANGE_ME \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5599d7b7..aee9ab1b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /.idea/ /.coverage /htmlcov/ +/.env diff --git a/intervention/settings.py b/intervention/settings.py index 8d3d624b..23f828ff 100644 --- a/intervention/settings.py +++ b/intervention/settings.py @@ -5,6 +5,8 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 30.11.20 """ +from konova.sub_settings.django_settings import env + INTERVENTION_IDENTIFIER_LENGTH = 6 INTERVENTION_IDENTIFIER_TEMPLATE = "EIV-{}" @@ -14,7 +16,7 @@ INTERVENTION_LANIS_LAYER_NAME_UNRECORDED_OLD_ENTRY = "eiv_unrecorded_old_entries # EGON connection settings via rabbitmq # NEEDED FOR BACKWARDS COMPATIBILITY -EGON_RABBITMQ_HOST = "CHANGE_ME" -EGON_RABBITMQ_PORT = "CHANGE_ME" -EGON_RABBITMQ_USER = "CHANGE_ME" -EGON_RABBITMQ_PW = "CHANGE_ME" +EGON_RABBITMQ_HOST = env("EGON_RABBITMQ_HOST") +EGON_RABBITMQ_PORT = env("EGON_RABBITMQ_PORT") +EGON_RABBITMQ_USER = env("EGON_RABBITMQ_USER") +EGON_RABBITMQ_PW = env("EGON_RABBITMQ_PW") diff --git a/konova/settings.py b/konova/settings.py index e1e0e46b..c1ad3b9c 100644 --- a/konova/settings.py +++ b/konova/settings.py @@ -18,7 +18,6 @@ from konova.sub_settings.proxy_settings import * from konova.sub_settings.sso_settings import * from konova.sub_settings.table_settings import * from konova.sub_settings.lanis_settings import * -from konova.sub_settings.wfs_parcel_settings import * from konova.sub_settings.logging_settings import * # Max upload size for POST forms diff --git a/konova/sso/sso.py b/konova/sso/sso.py deleted file mode 100644 index f3038428..00000000 --- a/konova/sso/sso.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -Author: Michel Peltriaux -Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany -Contact: michel.peltriaux@sgdnord.rlp.de -Created on: 17.08.21 - -""" -from django.http import HttpResponse -from django.urls import re_path -from django.views import View -from django.views.decorators.csrf import csrf_exempt -from itsdangerous import TimedSerializer -from simple_sso.sso_client.client import Client - -from user.models import User - - -class PropagateView(View): - """ View used to receive propagated sso-server user data - - """ - client = None - signer = None - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.signer = TimedSerializer(self.client.private_key) - - @csrf_exempt - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) - - def post(self, request): - user_data = request.body - user_data = self.signer.loads(user_data) - self.client.build_user(user_data) - return HttpResponse(status=200) - - -class KonovaSSOClient(Client): - """ Konova specialized derivative of general sso.Client. - - Adds some custom behaviour for konova usage. - - """ - propagate_view = PropagateView - - def get_urls(self): - urls = super().get_urls() - urls += re_path(r'^propagate/$', self.propagate_view.as_view(client=self), name='simple-sso-propagate'), - return urls - - def build_user(self, user_data): - """ Creates a user or updates user data - - Args: - user_data (): - - Returns: - - """ - try: - user = User.objects.get(username=user_data['username']) - # Update user data, excluding some changes - skipable_attrs = { - "username", - "is_staff", - "is_superuser", - } - for _attr, _val in user_data.items(): - if _attr in skipable_attrs: - continue - setattr(user, _attr, _val) - except User.DoesNotExist: - user = User(**user_data) - user.set_unusable_password() - user.save() - return user \ No newline at end of file diff --git a/konova/sub_settings/django_settings.py b/konova/sub_settings/django_settings.py index f6b3e134..1a5193a2 100644 --- a/konova/sub_settings/django_settings.py +++ b/konova/sub_settings/django_settings.py @@ -10,6 +10,8 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/3.1/ref/settings/ """ import os + +import environ from django.utils.translation import gettext_lazy as _ from django.conf.locale.de import formats as de_formats @@ -24,28 +26,24 @@ BASE_DIR = os.path.dirname( ) ) -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ +env = environ.Env() +# Take environment variables from .env.dev file +environ.Env.read_env(os.path.join(BASE_DIR, '.env')) # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '5=9-)2)h$u9=!zrhia9=lj-2#cpcb8=#$7y+)l$5tto$3q(n_+' +SECRET_KEY = env("SECRET_KEY") # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = env.bool("DEBUG", default=False) -ADMINS = [ - ('KSP-Servicestelle', 'ksp-servicestelle@sgdnord.rlp.de'), -] +ADMINS = [x.split(':') for x in env.list('ADMINS')] -BASE_URL = "http://localhost:8001" +ALLOWED_HOSTS = env.list("ALLOWED_HOSTS") -ALLOWED_HOSTS = [ - "127.0.0.1", - "localhost", -] +BASE_URL = env("BASE_URL") CSRF_TRUSTED_ORIGINS = [ - "http://localhost", # not only host but schema (http/s) as well! + BASE_URL ] # Authentication settings @@ -83,10 +81,6 @@ INSTALLED_APPS = [ 'analysis', 'api', ] -if DEBUG: - INSTALLED_APPS += [ - 'debug_toolbar', - ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', @@ -98,10 +92,6 @@ MIDDLEWARE = [ 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -if DEBUG: - MIDDLEWARE += [ - "debug_toolbar.middleware.DebugToolbarMiddleware", - ] ROOT_URLCONF = 'konova.urls' @@ -131,11 +121,11 @@ WSGI_APPLICATION = 'konova.wsgi.application' DATABASES = { 'default': { 'ENGINE': 'django.contrib.gis.db.backends.postgis', - 'NAME': os.environ.get('POSTGRES_NAME'), - 'USER': os.environ.get('POSTGRES_USER'), - 'HOST': os.environ.get('POSTGRES_HOST'), - 'PASSWORD': os.environ.get('POSTGRES_PASSWORD'), - 'PORT': os.environ.get('POSTGRES_PORT'), + 'NAME': env("DB_NAME"), + 'USER': env("DB_USER"), + 'PASSWORD': env("DB_PASSWORD"), + 'HOST': env("DB_HOST"), + 'PORT': env("DB_PORT"), } } DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" @@ -202,37 +192,16 @@ STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'templates/map/client/libs'), # NETGIS map client files ] -# DJANGO DEBUG TOOLBAR -INTERNAL_IPS = [ - "127.0.0.1" -] -DEBUG_TOOLBAR_CONFIG = { - "DISABLE_PANELS": { - 'debug_toolbar.panels.versions.VersionsPanel', - 'debug_toolbar.panels.timer.TimerPanel', - 'debug_toolbar.panels.settings.SettingsPanel', - 'debug_toolbar.panels.headers.HeadersPanel', - 'debug_toolbar.panels.request.RequestPanel', - 'debug_toolbar.panels.sql.SQLPanel', - 'debug_toolbar.panels.staticfiles.StaticFilesPanel', - 'debug_toolbar.panels.templates.TemplatesPanel', - 'debug_toolbar.panels.cache.CachePanel', - 'debug_toolbar.panels.signals.SignalsPanel', - 'debug_toolbar.panels.logging.LoggingPanel', - 'debug_toolbar.panels.redirects.RedirectsPanel', - 'debug_toolbar.panels.profiling.ProfilingPanel', - } -} - # EMAIL (see https://docs.djangoproject.com/en/dev/topics/email/) if DEBUG: # ONLY FOR DEVELOPMENT NEEDED EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend' EMAIL_FILE_PATH = '/tmp/app-messages' -DEFAULT_FROM_EMAIL = "no-reply@ksp.de" # The default email address for the 'from' element +DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL") # The default email address for the 'from' element SERVER_EMAIL = DEFAULT_FROM_EMAIL # The default email sender address, which is used by Django to send errors via mail -EMAIL_HOST = os.environ.get('SMTP_HOST') -EMAIL_REPLY_TO = os.environ.get('SMTP_REAL_REPLY_MAIL') -SUPPORT_MAIL_RECIPIENT = EMAIL_REPLY_TO -EMAIL_PORT = os.environ.get('SMTP_PORT') +EMAIL_HOST = env("SMTP_HOST") +EMAIL_REPLY_TO = env("REPLY_TO_ADDR") +EMAIL_PORT = env("SMTP_PORT") +EMAIL_USE_TLS = False +EMAIL_USE_SSL = False diff --git a/konova/sub_settings/proxy_settings.py b/konova/sub_settings/proxy_settings.py index 559344a7..aef3151b 100644 --- a/konova/sub_settings/proxy_settings.py +++ b/konova/sub_settings/proxy_settings.py @@ -5,12 +5,13 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 31.01.22 """ +from konova.sub_settings.django_settings import env -proxy = "" +proxy = env("PROXY") PROXIES = { "http": proxy, "https": proxy, } -CLIENT_PROXY_AUTH_USER = "CHANGE_ME" -CLIENT_PROXY_AUTH_PASSWORD = "CHANGE_ME" \ No newline at end of file +GEOPORTAL_RLP_USER = env("GEOPORTAL_RLP_USER") +GEOPORTAL_RLP_PASSWORD = env("GEOPORTAL_RLP_PASSWORD") diff --git a/konova/sub_settings/schneider_settings.py b/konova/sub_settings/schneider_settings.py index caf4ec28..920a0c3f 100644 --- a/konova/sub_settings/schneider_settings.py +++ b/konova/sub_settings/schneider_settings.py @@ -5,7 +5,8 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 14.12.22 """ +from konova.sub_settings.django_settings import env -base_url = "http://127.0.0.1:8002" -auth_header = "auth" -auth_header_token = "CHANGE_ME" +base_url = env("SCHNEIDER_BASE_URL") +auth_header = env("SCHNEIDER_AUTH_HEADER") +auth_header_token = env("SCHNEIDER_AUTH_TOKEN") diff --git a/konova/sub_settings/sso_settings.py b/konova/sub_settings/sso_settings.py index 84876bde..7ff66777 100644 --- a/konova/sub_settings/sso_settings.py +++ b/konova/sub_settings/sso_settings.py @@ -5,19 +5,14 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 31.01.22 """ -import random -import string -import os +from konova.sub_settings.django_settings import env - -# Django-simple-SSO settings -SSO_SERVER_BASE = f"http://{os.environ.get('SSO_HOST')}/" +# SSO settings +SSO_SERVER_BASE = env("SSO_SERVER_BASE_URL") SSO_SERVER = f"{SSO_SERVER_BASE}sso/" -SSO_PRIVATE_KEY = "CHANGE_ME" -SSO_PUBLIC_KEY = "CHANGE_ME" # OAuth settings -OAUTH_CODE_VERIFIER = "CHANGE_ME" +OAUTH_CODE_VERIFIER = env("OAUTH_CODE_VERIFIER") -OAUTH_CLIENT_ID = "CHANGE_ME" -OAUTH_CLIENT_SECRET = "CHANGE_ME" \ No newline at end of file +OAUTH_CLIENT_ID = env("OAUTH_CLIENT_ID") +OAUTH_CLIENT_SECRET = env("OAUTH_CLIENT_SECRET") diff --git a/konova/sub_settings/wfs_parcel_settings.py b/konova/sub_settings/wfs_parcel_settings.py deleted file mode 100644 index 37744a70..00000000 --- a/konova/sub_settings/wfs_parcel_settings.py +++ /dev/null @@ -1,12 +0,0 @@ -""" -Author: Michel Peltriaux -Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany -Contact: michel.peltriaux@sgdnord.rlp.de -Created on: 31.01.22 - -""" - -# Parcel WFS settings -PARCEL_WFS_BASE_URL = "https://www.geoportal.rlp.de/registry/wfs/519" -PARCEL_WFS_USER = "ksp" -PARCEL_WFS_PW = "CHANGE_ME" \ No newline at end of file diff --git a/konova/tasks.py b/konova/tasks.py index ea68cde0..d9045a0a 100644 --- a/konova/tasks.py +++ b/konova/tasks.py @@ -7,7 +7,7 @@ from django.core.exceptions import ObjectDoesNotExist @shared_task def celery_update_parcels(geometry_id: str, recheck: bool = True): - from konova.models import Geometry, ParcelIntersection + from konova.models import Geometry try: geom = Geometry.objects.get(id=geometry_id) geom.parcels.clear() diff --git a/konova/urls.py b/konova/urls.py index 227de543..8dc9a01f 100644 --- a/konova/urls.py +++ b/konova/urls.py @@ -13,22 +13,17 @@ Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ -import debug_toolbar from django.contrib import admin from django.urls import path, include -from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG -from konova.sso.sso import KonovaSSOClient from konova.views.logout import LogoutView from konova.views.geometry import GeomParcelsView, GeomParcelsContentView from konova.views.home import HomeView from konova.views.map_proxy import ClientProxyParcelSearch, ClientProxyParcelWFS from konova.views.oauth import OAuthLoginView, OAuthCallbackView -sso_client = KonovaSSOClient(SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY) urlpatterns = [ path('admin/', admin.site.urls), - path('login/', include(sso_client.get_urls())), path('oauth/callback/', OAuthCallbackView.as_view(), name="oauth-callback"), path('oauth/login/', OAuthLoginView.as_view(), name="oauth-login"), path('logout/', LogoutView.as_view(), name="logout"), @@ -47,10 +42,5 @@ urlpatterns = [ path('client/proxy/wfs', ClientProxyParcelWFS.as_view(), name="client-proxy-wfs"), ] -if DEBUG: - urlpatterns += [ - path('__debug__/', include(debug_toolbar.urls)), - ] - handler404 = "konova.views.error.get_404_view" handler500 = "konova.views.error.get_500_view" diff --git a/konova/utils/mailer.py b/konova/utils/mailer.py index d60f3b9a..bdb60614 100644 --- a/konova/utils/mailer.py +++ b/konova/utils/mailer.py @@ -9,7 +9,7 @@ from django.core.mail import send_mail from django.template.loader import render_to_string from django.utils.translation import gettext_lazy as _ -from konova.sub_settings.django_settings import DEFAULT_FROM_EMAIL, EMAIL_REPLY_TO, SUPPORT_MAIL_RECIPIENT +from konova.sub_settings.django_settings import DEFAULT_FROM_EMAIL, EMAIL_REPLY_TO class Mailer: @@ -416,7 +416,7 @@ class Mailer: "EMAIL_REPLY_TO": EMAIL_REPLY_TO, } msg = render_to_string("email/api/verify_token.html", context) - user_mail_address = [SUPPORT_MAIL_RECIPIENT] + user_mail_address = [EMAIL_REPLY_TO] self.send( user_mail_address, _("Request for new API token"), diff --git a/konova/utils/schneider/fetcher.py b/konova/utils/schneider/fetcher.py index 87aa7517..7e246bfd 100644 --- a/konova/utils/schneider/fetcher.py +++ b/konova/utils/schneider/fetcher.py @@ -11,6 +11,7 @@ from json import JSONDecodeError import requests from konova.sub_settings import schneider_settings +from konova.sub_settings.proxy_settings import PROXIES class ParcelFetcher: @@ -43,6 +44,7 @@ class ParcelFetcher: response = requests.post( url=post_url, + proxies=PROXIES, data=self.geojson, headers={ self.auth_header: self.auth_header_token diff --git a/konova/views/map_proxy.py b/konova/views/map_proxy.py index d6ebe03e..790ab8dc 100644 --- a/konova/views/map_proxy.py +++ b/konova/views/map_proxy.py @@ -18,7 +18,7 @@ from django.utils.translation import gettext_lazy as _ from requests.auth import HTTPDigestAuth -from konova.sub_settings.proxy_settings import PROXIES, CLIENT_PROXY_AUTH_USER, CLIENT_PROXY_AUTH_PASSWORD +from konova.sub_settings.proxy_settings import PROXIES, GEOPORTAL_RLP_USER, GEOPORTAL_RLP_PASSWORD class BaseClientProxyView(View): @@ -90,7 +90,7 @@ class ClientProxyParcelWFS(BaseClientProxyView): url = f"{base_url}?{urlencode(params, doseq=True)}" url = url.replace("typename", "typenames") - auth = HTTPDigestAuth(CLIENT_PROXY_AUTH_USER, CLIENT_PROXY_AUTH_PASSWORD) + auth = HTTPDigestAuth(GEOPORTAL_RLP_USER, GEOPORTAL_RLP_PASSWORD) content, response_code = self.perform_url_call(url, auth=auth) error_detected = response_code != 200 diff --git a/requirements.txt b/requirements.txt index e8e958c2..e1815ae8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,43 +4,41 @@ async-timeout==4.0.3 beautifulsoup4==4.13.0b2 billiard==4.2.0 cached-property==1.5.2 -celery==5.4.0rc2 -certifi==2024.2.2 -cffi==1.16.0 +celery==5.4.0 +certifi==2024.6.2 +cffi==1.17.0rc1 chardet==5.2.0 charset-normalizer==3.3.2 click==8.1.7 click-didyoumean==0.3.1 click-plugins==1.1.1 click-repl==0.3.0 -coverage==7.4.4 -cryptography==42.0.5 +coverage==7.5.3 +cryptography==42.0.8 Deprecated==1.2.14 -Django==5.0.4 +Django==5.0.6 django-autocomplete-light==3.11.0 django-bootstrap-modal-forms==3.0.4 -django-bootstrap4==24.1 -django-debug-toolbar==4.3.0 +django-bootstrap4==24.3 django-environ==0.11.2 django-filter==24.2 django-fontawesome-5==1.0.18 -django-oauth-toolkit==2.3.0 -django-simple-sso==1.2.0 +django-oauth-toolkit==2.4.0 django-tables2==2.7.0 et-xmlfile==1.1.0 +gunicorn==22.0.0 idna==3.7 importlib_metadata==7.1.0 -itsdangerous==0.24 jwcrypto==1.5.6 kombu==5.3.7 oauthlib==3.2.2 openpyxl==3.2.0b1 -packaging==24.0 +packaging==24.1 pika==1.3.2 -pillow==10.2.0 -prompt-toolkit==3.0.43 -psycopg==3.1.18 -psycopg-binary==3.1.18 +pillow==10.3.0 +prompt_toolkit==3.0.47 +psycopg==3.1.19 +psycopg-binary==3.1.19 pycparser==2.22 pyparsing==3.1.2 pypng==0.20220715.0 @@ -49,18 +47,16 @@ python-dateutil==2.9.0.post0 pytz==2024.1 PyYAML==6.0.1 qrcode==7.3.1 -redis==5.1.0b4 -requests==2.31.0 +redis==5.1.0b6 +requests==2.32.3 six==1.16.0 soupsieve==2.5 -sqlparse==0.4.4 -typing_extensions==4.11.0 +sqlparse==0.5.0 +typing_extensions==4.12.2 tzdata==2024.1 urllib3==2.2.1 vine==5.1.0 wcwidth==0.2.13 -webservices==0.7 wrapt==1.16.0 xmltodict==0.13.0 -zipp==3.18.1 -gunicorn==21.2.0 \ No newline at end of file +zipp==3.19.2 diff --git a/templates/map/client/config.json b/templates/map/client/config.json index 23efd2da..918ba3a4 100644 --- a/templates/map/client/config.json +++ b/templates/map/client/config.json @@ -112,7 +112,7 @@ }, "import": { - "geopackageLibURL": "/libs/geopackage/4.2.3/" + "geopackageLibURL": "/static/libs/geopackage/4.2.3/" }, "export": {