Parcourir la source

feat: Aktualisierung der Nginx- und Docker-Konfiguration für Keycloak und Paperless

- Hinzugefügt: Secure Cookie Flags (secure, samesite=lax) für Keycloak.
- Überarbeitet: Keycloak-Nginx-Locations für token und realms zur verbesserten Proxy-Konfiguration.
- Bereinigt: Entfernte überflüssige Sicherheitsheader und OIDC-Konfigurationen in Paperless.
- Aktualisiert: Nginx-Upstream-IP für Paperless angepasst.
- Hinzugefügt: Unterstützung für WebSocket-Verbindungen in der Paperless-Nginx-Konfiguration.
- Neu: Konfiguration für benutzerdefinierte Paperless-Einstellungen in Docker-Compose hinzugefügt.
- Verbesserte Sicherheit: CSRF Trusted Origins und erweiterte DNS-Einstellungen für Paperless ergänzt.
- Verschoben: Root-Location in Keycloak ans Ende für klarere Priorisierung.
- Gelöscht: Veraltete `custom_settings.py` für Paperless entfernt.

Diese Änderungen verbessern die Sicherheit und Kompatibilität der Dienste.
mrx8086 il y a 11 mois
Parent
commit
064bb1eead

+ 11 - 7
config/nginx/sites-available/keycloak

@@ -44,11 +44,9 @@ server {
     proxy_set_header Host $host;
     proxy_http_version 1.1;
 
-    # Root location for the main application
-    location / {
-        proxy_pass http://keycloak_upstream;
-    }
-
+    # Cookies sicher machen
+    proxy_cookie_flags ~ secure samesite=lax;
+    
     # Specific location for the token endpoint
     location ~ ^/auth/realms/[^/]+/protocol/openid-connect/token$ {
         proxy_pass http://keycloak_upstream;
@@ -65,8 +63,8 @@ server {
     }
 
     # Keycloak required paths
-    location /realms/ {
-        proxy_pass http://keycloak_upstream;
+    location ~ ^/realms/ {
+       proxy_pass http://keycloak_upstream;
         proxy_buffer_size 128k;
         proxy_buffers 4 256k;
         proxy_busy_buffers_size 256k;
@@ -79,6 +77,7 @@ server {
         proxy_read_timeout 60s;
     }
 
+
     location /resources/ {
         proxy_pass http://keycloak_upstream;
 
@@ -124,4 +123,9 @@ server {
         deny all;
         return 404;
     }
+
+    # Root location for the main application - this needs to be last
+    location / {
+        proxy_pass http://keycloak_upstream;
+    }
 }

+ 20 - 27
config/nginx/sites-available/paperless

@@ -1,5 +1,5 @@
 upstream paperless_upstream {
-    server 172.18.0.4:8000;  # SICHERSTELLEN, DASS DIES DIE KORREKTE IP UND DER PORT IST
+    server 172.20.0.4:8000;
 }
 
 server {
@@ -16,46 +16,39 @@ server {
     ssl_certificate /etc/nginx/ssl/mrx8086.com/fullchain.pem;
     ssl_certificate_key /etc/nginx/ssl/mrx8086.com/privkey.pem;
 
+    # Essential security settings
     ssl_protocols TLSv1.2 TLSv1.3;
-    ssl_prefer_server_ciphers on;
-    ssl_ciphers 'TLS-CHACHA20-POLY1305-SHA256:TLS-AES-256-GCM-SHA384:TLS-AES-128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305';
-    ssl_session_timeout 1d;
-    ssl_session_cache shared:MozSSL:10m;
-    ssl_session_tickets off;
-
-    # Security headers
-    add_header X-Content-Type-Options nosniff always;
-    add_header X-XSS-Protection "1; mode=block" always;
-    add_header X-Frame-Options SAMEORIGIN always;
-    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
-    add_header Content-Security-Policy "frame-ancestors 'self'; default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; media-src 'self';" always;
-
-    # Proxy settings
+
+    # Global proxy settings (important for Django)
+    proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header X-Forwarded-Proto $scheme;
-    proxy_set_header X-Forwarded-Host $host;
-    proxy_set_header X-Forwarded-Port 443;
-    proxy_set_header Host $host;
     proxy_http_version 1.1;
+    proxy_redirect off;
 
-    # Paperless specific settings
+    # Buffer settings
+    proxy_buffer_size 128k;
+    proxy_buffers 4 256k;
+    proxy_busy_buffers_size 256k;
+
+    # Required for document upload
     client_max_body_size 512M;
-    fastcgi_buffers 64 4K;
 
-    # Root location
+    # Main application
     location / {
         proxy_pass http://paperless_upstream;
-        proxy_set_header Upgrade $http_upgrade;
-        proxy_set_header Connection "upgrade";
         proxy_connect_timeout 60s;
         proxy_send_timeout 60s;
         proxy_read_timeout 60s;
     }
 
-    # Deny access to hidden files
-    location ~ /\. {
-        deny all;
-        return 404;
+    # WebSocket support (required for live updates)
+    location /ws {
+        proxy_pass http://paperless_upstream;
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection "upgrade";
+        proxy_read_timeout 86400;
     }
 }

+ 139 - 0
config/paperless/src/custom_settings.py

@@ -0,0 +1,139 @@
+from pathlib import Path
+import os
+import ssl
+
+# Import base Paperless settings first
+from paperless.settings import *
+
+print("="*50)
+print("CUSTOM SETTINGS IS BEING LOADED!!!!")
+print("="*50)
+
+print("Loading custom OIDC settings...")
+
+# Authentication backend configuration
+AUTHENTICATION_BACKENDS = [
+    'mozilla_django_oidc.auth.OIDCAuthenticationBackend',
+    'django.contrib.auth.backends.ModelBackend',
+    'guardian.backends.ObjectPermissionBackend',
+]
+
+# Extend existing INSTALLED_APPS
+INSTALLED_APPS = list(INSTALLED_APPS) + [
+    'mozilla_django_oidc',
+]
+
+# Extend existing MIDDLEWARE
+MIDDLEWARE = list(MIDDLEWARE) + [
+    'mozilla_django_oidc.middleware.SessionRefresh',
+    'middleware.LoginRequiredMiddleware',  # Custom middleware
+]
+
+# OpenID Connect Basic Configuration
+OIDC_ENABLED = True
+OIDC_DEFAULT_REDIRECT = True
+OIDC_AUTHORIZATION_CALLBACK = True
+
+# Authentication URLs
+LOGIN_URL = 'https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/auth'  # Direkt zu Keycloak
+LOGIN_REDIRECT_URL = '/'  # Weiterleitung zur Startseite nach der Anmeldung
+LOGOUT_REDIRECT_URL = 'https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/logout'  # Keycloak-Logout-URL
+
+# Additional OIDC Configuration
+OIDC_AUTHENTICATE_CLASS = 'mozilla_django_oidc.views.OIDCAuthenticationRequestView'
+OIDC_CALLBACK_CLASS = 'mozilla_django_oidc.views.OIDCAuthenticationCallbackView'
+OIDC_STORE_ACCESS_TOKEN = True
+OIDC_STORE_ID_TOKEN = True
+OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = 3600
+
+# OIDC Endpoints
+OIDC_OP_AUTHORIZATION_ENDPOINT = 'https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/auth'
+OIDC_OP_TOKEN_ENDPOINT = 'https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/token'
+OIDC_OP_USER_ENDPOINT = 'https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/userinfo'
+OIDC_OP_JWKS_ENDPOINT = 'https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/certs'
+
+# OIDC Client Configuration
+OIDC_RP_CLIENT_ID = 'paperless'
+OIDC_RP_CLIENT_SECRET = 'CSbw9ldbZBUGGQSJoAUEg10QKgjdb6Tq'  # Ersetzen Sie dies durch Ihr tatsächliches Client-Geheimnis
+OIDC_RP_SCOPES = 'openid email profile'  # Erforderliche Scopes (openid ist zwingend erforderlich)
+
+# OIDC Authentication Callback URL
+OIDC_AUTHENTICATION_CALLBACK_URL = 'https://docs.mrx8086.com/oidc/callback/'
+
+# OIDC User Creation and Claims
+OIDC_CREATE_USER = True
+OIDC_USERNAME_CLAIM = 'preferred_username'
+OIDC_EMAIL_CLAIM = 'email'
+
+# OIDC Algorithm and Nonce
+OIDC_RP_SIGN_ALGO = 'RS256'
+OIDC_USE_NONCE = True
+
+# OIDC Exempt URLs
+OIDC_EXEMPT_URLS = [
+    "/oidc/callback/",  # Callback-Pfad für OIDC
+    "/oidc/authenticate/",  # OIDC Authentifizierungs-URL
+]
+
+# Login Required Middleware Configuration
+LOGIN_REQUIRED_MIDDLEWARE = {
+    'IGNORE_PATHS': [
+        '/static/',
+        '/oidc/callback/',  # Callback-Pfad darf nicht blockiert werden
+        '/oidc/authenticate/',  # OIDC Authentifizierungs-URL
+    ],
+}
+
+# Session Configuration
+SESSION_COOKIE_SECURE = True  # Nur über HTTPS senden
+SESSION_COOKIE_SAMESITE = 'Lax'  # Schutz vor CSRF-Angriffen
+SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # Verwende Cache für Sessions
+SESSION_CACHE_ALIAS = 'default'
+
+# Detailed Logging Configuration
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'formatters': {
+        'verbose': {
+            'format': '%(levelname)s [%(asctime)s] %(name)s: %(message)s'
+        },
+    },
+    'handlers': {
+        'console': {
+            'class': 'logging.StreamHandler',
+            'formatter': 'verbose',
+        },
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['console'],
+            'level': 'DEBUG',
+            'propagate': True,
+        },
+        'paperless': {
+            'handlers': ['console'],
+            'level': 'DEBUG',
+            'propagate': True,
+        },
+        'paperless.middleware': {  # Middleware-spezifische Logs
+            'handlers': ['console'],
+            'level': 'DEBUG',
+            'propagate': True,
+        },
+        'mozilla_django_oidc': {  # OIDC-spezifische Logs
+            'handlers': ['console'],
+            'level': 'DEBUG',
+            'propagate': True,
+        },
+    },
+}
+
+# SSL Configuration (Development Only)
+ssl._create_default_https_context = ssl._create_unverified_context
+
+# Additional Security Settings
+CSRF_TRUSTED_ORIGINS = ['https://docs.mrx8086.com']
+SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
+CSRF_COOKIE_SECURE = True  # Nur über HTTPS senden
+SECURE_SSL_REDIRECT = True  # Alle Anfragen auf HTTPS umleiten

+ 41 - 0
config/paperless/src/middleware.py

@@ -0,0 +1,41 @@
+import logging
+from django.utils.deprecation import MiddlewareMixin
+from django.shortcuts import redirect
+from django.conf import settings
+
+# Logger für die Middleware
+logger = logging.getLogger('paperless.middleware')
+
+class LoginRequiredMiddleware(MiddlewareMixin):
+    """
+    Middleware, die sicherstellt, dass Benutzer authentifiziert sind, bevor sie auf bestimmte Pfade zugreifen.
+    Nicht authentifizierte Benutzer werden zur Keycloak-Login-Seite weitergeleitet.
+    """
+
+    def process_request(self, request):
+        # Pfade, die ignoriert werden sollen (z. B. Callback-Pfad)
+        ignore_paths = [
+            '/oidc/callback/',  # Callback-Pfad für OIDC
+            '/static/',         # Statische Dateien
+            '/oidc/authenticate/',  # OIDC Authentifizierungs-URL
+        ]
+
+        # Überprüfe, ob der aktuelle Pfad ignoriert werden soll
+        if any(request.path.startswith(path) for path in ignore_paths):
+            logger.debug(f"Ignoring path: {request.path}")  # Logge den ignorierten Pfad
+            return None  # Keine Weiterleitung für diese Pfade
+
+        # Überprüfe, ob der Benutzer authentifiziert ist
+        if not request.user.is_authenticated:
+            # Baue die Redirect-URL mit den erforderlichen Parametern
+            redirect_url = (
+                f"{settings.LOGIN_URL}?"
+                f"client_id={settings.OIDC_RP_CLIENT_ID}&"
+                f"redirect_uri={settings.OIDC_AUTHENTICATION_CALLBACK_URL}&"
+                f"response_type=code&"
+                f"scope={settings.OIDC_RP_SCOPES}"
+            )
+            logger.debug(f"Redirecting to: {redirect_url}")  # Logge die Redirect-URL
+            return redirect(redirect_url)  # Leite den Benutzer zur Keycloak-Login-Seite um
+
+        return None

+ 0 - 28
config/paperless/src/paperless/custom_settings.py

@@ -1,28 +0,0 @@
-from pathlib import Path
-import os
-
-print("Loading custom OIDC settings...")
-
-# Use Django's built-in OIDC auth backend
-AUTHENTICATION_BACKENDS = [
-    'mozilla_django_oidc.auth.OIDCAuthenticationBackend',
-    'django.contrib.auth.backends.ModelBackend',
-]
-
-# Add mozilla_django_oidc to INSTALLED_APPS
-INSTALLED_APPS = [
-    'mozilla_django_oidc',
-]
-
-OIDC_ENABLED = True
-OIDC_DEFAULT_REDIRECT = True
-
-LOGIN_URL = "/oidc/authenticate/"
-LOGIN_REDIRECT_URL = "/"
-LOGOUT_REDIRECT_URL = "/"
-
-# Debug settings
-import logging
-logger = logging.getLogger('mozilla_django_oidc')
-logger.addHandler(logging.StreamHandler())
-logger.setLevel(logging.DEBUG)

+ 16 - 21
docker/docker-compose.yml

@@ -35,7 +35,8 @@ services:
     depends_on:
       - keycloak-db
     extra_hosts:
-      - "cloud.mrx8086.com:172.23.171.133"         
+      - "cloud.mrx8086.com:172.23.171.133"    
+      - "docs.mrx8086.com:172.23.171.133"     
     healthcheck:
       test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
       interval: 30s
@@ -118,7 +119,9 @@ services:
       - nextcloud-network
 
   paperless:
-    image: ghcr.io/paperless-ngx/paperless-ngx:latest
+    build:
+      context: .
+      dockerfile: paperless.Containerfile
     container_name: paperless
     restart: unless-stopped
     ports:
@@ -128,6 +131,8 @@ services:
       - ../config/paperless/media:/usr/src/paperless/media
       - ../config/paperless/export:/usr/src/paperless/export
       - ../config/paperless/consume:/usr/src/paperless/consume
+      - ../config/paperless/src/custom_settings.py:/usr/src/paperless/custom_settings.py
+      - ../config/paperless/src/middleware.py:/usr/src/paperless/middleware.py
     environment:
       # Base Configuration
       - PAPERLESS_SECRET_KEY=${PAPERLESS_SECRET_KEY}
@@ -138,32 +143,18 @@ services:
       - PAPERLESS_DBUSER=${PAPERLESS_DB_USER}
       - PAPERLESS_DBPASS=${PAPERLESS_DB_PASSWORD}
       - PAPERLESS_URL=https://docs.mrx8086.com
-      - PAPERLESS_ALLOWED_HOSTS=docs.mrx8086.com
-      
+      - PAPERLESS_ALLOWED_HOSTS=*
+      - PAPERLESS_FORCE_SCRIPT_NAME=
+      - PAPERLESS_CSRF_TRUSTED_ORIGINS=https://docs.mrx8086.com
       # Authentication Configuration
       - PAPERLESS_NO_NATIVE_AUTH=true
       - PAPERLESS_DISABLE_PASSWORD_LOGIN=true
-      
-      # Standard OpenID Connect Configuration
-      - OIDC_RP_CLIENT_ID=paperless
-      - OIDC_RP_CLIENT_SECRET=${PAPERLESS_CLIENT_SECRET}
-      - OIDC_OP_AUTHORIZATION_ENDPOINT=https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/auth
-      - OIDC_OP_TOKEN_ENDPOINT=https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/token
-      - OIDC_OP_USER_ENDPOINT=https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/userinfo
-      - OIDC_OP_JWKS_ENDPOINT=https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/certs
-      
-      # Additional OpenID Settings
-      - OIDC_VERIFY_SSL=false
-      - OIDC_AUTHENTICATION_CALLBACK_URL=https://docs.mrx8086.com/oidc/callback/
-      - OIDC_CREATE_USER=true
-      - OIDC_USERNAME_CLAIM=preferred_username
-      - OIDC_EMAIL_CLAIM=email
-      
       # Debug settings
       - PAPERLESS_DEBUG=true
       - PAPERLESS_LOG_LEVEL=DEBUG
       - DJANGO_LOG_LEVEL=DEBUG
-      
+      - PYTHONPATH=/usr/src/paperless
+      - DJANGO_SETTINGS_MODULE=custom_settings
     depends_on:
       - paperless-db
       - paperless-redis
@@ -171,6 +162,10 @@ services:
       - paperless-network
     extra_hosts:
       - "auth.mrx8086.com:172.23.171.133"
+      - "docs.mrx8086.com:172.23.171.133"
+    dns:
+      - 8.8.8.8
+      - 8.8.4.4
     healthcheck:
       test: ["CMD", "curl", "-f", "http://localhost:8000/"]
       interval: 30s

+ 7 - 0
docker/paperless.Containerfile

@@ -0,0 +1,7 @@
+FROM ghcr.io/paperless-ngx/paperless-ngx:latest
+
+# Set up DNS resolution and ensure network connectivity
+RUN pip install --upgrade pip && \
+    pip install --no-cache-dir mozilla-django-oidc
+
+# The base image already has the correct ENTRYPOINT/CMD

+ 0 - 1244
git_diff.txt

@@ -1,1244 +0,0 @@
-diff --git a/.gitignore b/.gitignore
-index 8994b5d..a054b2f 100644
---- a/.gitignore
-+++ b/.gitignore
-@@ -1,3 +1,4 @@
- data/
- config/credentials
- config/nextcloud/
-+scripts/setup/paperless/node_modules
-\ No newline at end of file
-diff --git a/ansible/roles/services/defaults/main.yml b/ansible/roles/services/defaults/main.yml
-index 3555dd9..168ea0f 100644
---- a/ansible/roles/services/defaults/main.yml
-+++ b/ansible/roles/services/defaults/main.yml
-@@ -20,4 +20,13 @@ sso_config:
-         nextcloud-youpi: "youpi"
- 
- # Default paths and settings
--nextcloud_data_dir: "/var/www/html/data"
-\ No newline at end of file
-+nextcloud_data_dir: "/var/www/html/data"
-+
-+# ansible/roles/services/defaults/main.yml
-+paperless_oidc:
-+  client_id: paperless
-+  provider_url: "https://{{ keycloak_host }}"
-+  realm: "{{ keycloak_realm }}"
-+  sign_algo: "RS256"
-+  verify_ssl: false
-+  scopes: "openid profile email"
-\ No newline at end of file
-diff --git a/ansible/roles/services/tasks/main.yml b/ansible/roles/services/tasks/main.yml
-index c6f13f1..c64d421 100644
---- a/ansible/roles/services/tasks/main.yml
-+++ b/ansible/roles/services/tasks/main.yml
-@@ -120,4 +120,15 @@
- 
- - name: "Display SSO configuration"
-   debug:
--    var: sso_config_verification.stdout
-\ No newline at end of file
-+    var: sso_config_verification.stdout
-+
-+# ansible/roles/services/tasks/main.yml
-+- name: Configure Paperless
-+  block:
-+    - name: Setup Paperless Django settings
-+      template:
-+        src: paperless_django_settings.j2
-+        dest: "{{ paperless_config_dir }}/django/settings.py"
-+      tags: 
-+        - paperless
-+        - paperless-config
-\ No newline at end of file
-diff --git a/ansible/vars/defaults/main.yml b/ansible/vars/defaults/main.yml
-index 93934a6..e14a77f 100644
---- a/ansible/vars/defaults/main.yml
-+++ b/ansible/vars/defaults/main.yml
-@@ -19,3 +19,9 @@ nodered_port: 1880
- 
- # Docker-Konfiguration
- docker_compose_version: "2.21.0"
-+
-+# Paperless Variables
-+paperless_base_dir: /path/to/your/paperless
-+paperless_oidc_client_id: paperless
-+keycloak_host: auth.mrx8086.com
-+keycloak_realm: office-automation
-\ No newline at end of file
-diff --git a/config/nginx/sites-available/keycloak b/config/nginx/sites-available/keycloak
-index cf437a2..a24450e 100644
---- a/config/nginx/sites-available/keycloak
-+++ b/config/nginx/sites-available/keycloak
-@@ -1,5 +1,5 @@
- upstream keycloak_upstream {
--    server 172.18.0.3:8080;
-+    server 172.19.0.3:8080;
- }
- 
- server {
-diff --git a/config/nginx/sites-available/nextcloud b/config/nginx/sites-available/nextcloud
-index 8357df3..3c24334 100644
---- a/config/nginx/sites-available/nextcloud
-+++ b/config/nginx/sites-available/nextcloud
-@@ -1,5 +1,5 @@
- upstream nextcloud_upstream {
--    server 172.19.0.3:80;  # SICHERSTELLEN, DASS DIES DIE KORREKTE IP IST
-+    server 172.20.0.3:80;  # SICHERSTELLEN, DASS DIES DIE KORREKTE IP IST
- }
- 
- server {
-diff --git a/config/nginx/sites-available/paperless b/config/nginx/sites-available/paperless
-index 6a7b80c..41563c5 100644
---- a/config/nginx/sites-available/paperless
-+++ b/config/nginx/sites-available/paperless
-@@ -1,41 +1,61 @@
--# HTTP-Weiterleitung auf HTTPS
-+upstream paperless_upstream {
-+    server 172.18.0.4:8000;  # SICHERSTELLEN, DASS DIES DIE KORREKTE IP UND DER PORT IST
-+}
-+
- server {
-     listen 80;
-     server_name docs.mrx8086.com;
--
--    # Weiterleitung von HTTP zu HTTPS
-     return 301 https://$host$request_uri;
- }
- 
--# HTTPS-Server
- server {
-     listen 443 ssl;
-     server_name docs.mrx8086.com;
- 
--    # SSL-Zertifikate einbinden
-+    # SSL Configuration
-     ssl_certificate /etc/nginx/ssl/mrx8086.com/fullchain.pem;
-     ssl_certificate_key /etc/nginx/ssl/mrx8086.com/privkey.pem;
- 
--    # Empfohlene SSL-Einstellungen (optional)
-     ssl_protocols TLSv1.2 TLSv1.3;
-     ssl_prefer_server_ciphers on;
--    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
--    ssl_session_cache shared:SSL:10m;
--    ssl_session_timeout 10m;
-+    ssl_ciphers 'TLS-CHACHA20-POLY1305-SHA256:TLS-AES-256-GCM-SHA384:TLS-AES-128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305';
-+    ssl_session_timeout 1d;
-+    ssl_session_cache shared:MozSSL:10m;
-+    ssl_session_tickets off;
- 
-+    # Security headers
-+    add_header X-Content-Type-Options nosniff always;
-+    add_header X-XSS-Protection "1; mode=block" always;
-+    add_header X-Frame-Options SAMEORIGIN always;
-     add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
--    add_header X-Content-Type-Options nosniff;
--    add_header X-Frame-Options DENY;
--    add_header X-XSS-Protection "1; mode=block";
-+    add_header Content-Security-Policy "frame-ancestors 'self'; default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; media-src 'self';" always;
-+
-+    # Proxy settings
-+    proxy_set_header X-Real-IP $remote_addr;
-+    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-+    proxy_set_header X-Forwarded-Proto $scheme;
-+    proxy_set_header X-Forwarded-Host $host;
-+    proxy_set_header X-Forwarded-Port 443;
-+    proxy_set_header Host $host;
-+    proxy_http_version 1.1;
- 
--    # Paperless-NGX Konfiguration
-+    # Paperless specific settings
-+    client_max_body_size 512M;
-+    fastcgi_buffers 64 4K;
-+
-+    # Root location
-     location / {
--        proxy_pass http://127.0.0.1:8000;  # Paperless läuft auf Port 8000 innerhalb des Docker-Containers
--        proxy_set_header Host $host;
--        proxy_set_header X-Real-IP $remote_addr;
--        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
--        proxy_set_header X-Forwarded-Proto https;
--        proxy_set_header X-Forwarded-Port 443;
-+        proxy_pass http://paperless_upstream;
-+        proxy_set_header Upgrade $http_upgrade;
-+        proxy_set_header Connection "upgrade";
-+        proxy_connect_timeout 60s;
-+        proxy_send_timeout 60s;
-+        proxy_read_timeout 60s;
-     }
--}
- 
-+    # Deny access to hidden files
-+    location ~ /\. {
-+        deny all;
-+        return 404;
-+    }
-+}
-\ No newline at end of file
-diff --git a/docker/.env b/docker/.env
-index 2a7c926..0993b0b 100644
---- a/docker/.env
-+++ b/docker/.env
-@@ -11,4 +11,49 @@ NEXTCLOUD_DB_USER=nextcloud
- NEXTCLOUD_DB_PASSWORD=YeTn4f1IIM9a7I3Q7oZNaEhs
- NEXTCLOUD_DB_ROOT_PASSWORD=rY26aXw9uMOqz2BjtevQ4oKB
- NEXTCLOUD_ADMIN_USER=admin
--NEXTCLOUD_ADMIN_PASSWORD=jTjRBEJb2ZSAH0iJoqeZijYL
-\ No newline at end of file
-+NEXTCLOUD_ADMIN_PASSWORD=jTjRBEJb2ZSAH0iJoqeZijYL
-+
-+
-+
-+# ==============================================================================
-+# Paperless Environment Variables
-+# ==============================================================================
-+
-+# Datenbank-Konfiguration
-+# ------------------------------------------------------------------------------
-+# Benutzername für die Paperless-Datenbank
-+PAPERLESS_DB_USER=paperless
-+
-+# Passwort für die Paperless-Datenbank
-+PAPERLESS_DB_PASSWORD=sq812ylB1Lfk49xbP8xxNSDA
-+
-+# Datenbank Name
-+PAPERLESS_DB_NAME=paperless
-+
-+# Paperless Admin User
-+# ------------------------------------------------------------------------------
-+# Benutzername für den Paperless Admin Account
-+PAPERLESS_ADMIN_USER=admin
-+
-+# Passwort für den Paperless Admin Account
-+PAPERLESS_ADMIN_PASSWORD=GRnrM1lrMl63E16HgZk8PBXU
-+
-+# Paperless Secret Key
-+# ------------------------------------------------------------------------------
-+# Geheimer Schlüssel für Paperless (wird für Django benötigt)
-+# Sollte ein zufälliger, sicherer String sein
-+PAPERLESS_SECRET_KEY=qfQ7MeGB79F6nbTgFk99K2Nx
-+
-+# Paperless OpenID Connect (Keycloak) Konfiguration
-+# ------------------------------------------------------------------------------
-+# Client ID für Paperless in Keycloak
-+PAPERLESS_CLIENT_ID=paperless
-+
-+# Client Secret für Paperless in Keycloak
-+PAPERLESS_CLIENT_SECRET=CSbw9ldbZBUGGQSJoAUEg10QKgjdb6Tq
-+
-+# Die URL von Paperless
-+PAPERLESS_URL=https://docs.mrx8086.com
-+
-+# Erlaubte Hosts für Paperless
-+PAPERLESS_ALLOWED_HOSTS=docs.mrx8086.com
-\ No newline at end of file
-diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
-index ccbcd3b..d3bf016 100644
---- a/docker/docker-compose.yml
-+++ b/docker/docker-compose.yml
-@@ -117,8 +117,102 @@ services:
-     networks:
-       - nextcloud-network
- 
-+  paperless:
-+    image: ghcr.io/paperless-ngx/paperless-ngx:latest
-+    container_name: paperless
-+    restart: unless-stopped
-+    ports:
-+      - "8000:8000"
-+    volumes:
-+      - ../data/paperless:/usr/src/paperless/data
-+      - ../config/paperless/media:/usr/src/paperless/media
-+      - ../config/paperless/export:/usr/src/paperless/export
-+      - ../config/paperless/consume:/usr/src/paperless/consume
-+    environment:
-+      # Basis-Konfiguration
-+      - PAPERLESS_ADMIN_USER=${PAPERLESS_ADMIN_USER}
-+      - PAPERLESS_ADMIN_PASSWORD=${PAPERLESS_ADMIN_PASSWORD}
-+      - PAPERLESS_SECRET_KEY=${PAPERLESS_SECRET_KEY}
-+      - PAPERLESS_URL=https://docs.mrx8086.com
-+      - PAPERLESS_ALLOWED_HOSTS=docs.mrx8086.com
-+      - PAPERLESS_REDIS=redis://paperless-redis:6379
-+      - PAPERLESS_LOGGING_DIR=/dev/stdout
-+      - PAPERLESS_LOGGING_LEVEL=DEBUG
-+      - DJANGO_LOG_LEVEL=DEBUG
-+      
-+      # OIDC Basis-Einstellungen
-+      - PAPERLESS_ENABLE_OIDC=true
-+      - PAPERLESS_OIDC_RP_PROVIDER_URL=https://auth.mrx8086.com/realms/office-automation
-+      - PAPERLESS_OIDC_RP_CLIENT_ID=paperless
-+      - PAPERLESS_OIDC_RP_CLIENT_SECRET=${PAPERLESS_CLIENT_SECRET}
-+      
-+      # OIDC Endpoints
-+      - PAPERLESS_OIDC_AUTH_ENDPOINT=https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/auth
-+      - PAPERLESS_OIDC_TOKEN_ENDPOINT=https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/token
-+      - PAPERLESS_OIDC_USERINFO_ENDPOINT=https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/userinfo
-+      - PAPERLESS_OIDC_JWKS_ENDPOINT=https://auth.mrx8086.com/realms/office-automation/protocol/openid-connect/certs
-+      
-+      # OIDC Claims und Scopes
-+      - PAPERLESS_OIDC_RP_SCOPE=openid profile email
-+      - PAPERLESS_OIDC_RP_USERNAME_CLAIM=preferred_username
-+      - PAPERLESS_OIDC_RP_NAME_CLAIM=name
-+      - PAPERLESS_OIDC_RP_EMAIL_CLAIM=email
-+      
-+      # OIDC Sicherheitseinstellungen
-+      - PAPERLESS_OIDC_RP_SIGN_ALGO=RS256
-+      - PAPERLESS_OIDC_RP_VERIFY_SSL=false
-+      - PAPERLESS_OIDC_USE_PKCE=true
-+      
-+      # OIDC Token-Management
-+      - PAPERLESS_OIDC_RP_RENEW_TOKEN_BEFORE_EXPIRY=true
-+    
-+    depends_on:
-+      - paperless-db
-+      - paperless-redis
-+    networks:
-+      - paperless-network
-+    extra_hosts:
-+      - "auth.mrx8086.com:172.23.171.133"
-+    healthcheck:
-+      test: ["CMD", "curl", "-f", "http://localhost:8000/"]
-+      interval: 30s
-+      timeout: 10s
-+      retries: 3
-+
-+  paperless-db:
-+    image: postgres:15
-+    container_name: paperless-db
-+    restart: unless-stopped
-+    environment:
-+      POSTGRES_USER: ${PAPERLESS_DB_USER}
-+      POSTGRES_PASSWORD: ${PAPERLESS_DB_PASSWORD}
-+      POSTGRES_DB: paperless
-+    volumes:
-+      - ../data/paperless-db:/var/lib/postgresql/data
-+    networks:
-+      - paperless-network
-+    healthcheck:
-+      test: ["CMD-SHELL", "pg_isready -U ${PAPERLESS_DB_USER} -d paperless"]
-+      interval: 10s
-+      timeout: 5s
-+      retries: 5
-+  
-+  paperless-redis:
-+    image: redis:7
-+    container_name: paperless-redis
-+    restart: unless-stopped
-+    networks:
-+      - paperless-network
-+    healthcheck:
-+      test: ["CMD", "redis-cli", "ping"]
-+      interval: 10s
-+      timeout: 5s
-+      retries: 5
-+
- networks:
-   keycloak-network:
-     driver: bridge
-   nextcloud-network:
-+    driver: bridge
-+  paperless-network:
-     driver: bridge
-\ No newline at end of file
-diff --git a/docs/context/configuration/docker-compose.yml b/docs/context/configuration/docker-compose.yml
-index 86b29d1..d36b088 100644
---- a/docs/context/configuration/docker-compose.yml
-+++ b/docs/context/configuration/docker-compose.yml
-@@ -100,8 +100,66 @@ services:
-     networks:
-       - nextcloud-network
- 
-+  paperless:
-+    image: ghcr.io/paperless-ngx/paperless-ngx:latest
-+    container_name: paperless
-+    restart: unless-stopped
-+    ports:
-+      - "8000:8000"
-+    volumes:
-+      - ../data/paperless:/usr/src/paperless/data
-+      - ../config/paperless/media:/usr/src/paperless/media
-+      - ../config/paperless/export:/usr/src/paperless/export
-+      - ../config/paperless/consume:/usr/src/paperless/consume
-+    environment:
-+      - PAPERLESS_ADMIN_USER=${PAPERLESS_ADMIN_USER}
-+      - PAPERLESS_ADMIN_PASSWORD=${PAPERLESS_ADMIN_PASSWORD}
-+      - PAPERLESS_SECRET_KEY=${PAPERLESS_SECRET_KEY}
-+      - PAPERLESS_URL=https://docs.mrx8086.com
-+      - PAPERLESS_ALLOWED_HOSTS=docs.mrx8086.com
-+      - PAPERLESS_ENABLE_OIDC=true
-+      - PAPERLESS_OIDC_RP_CLIENT_ID=${PAPERLESS_CLIENT_ID}
-+      - PAPERLESS_OIDC_RP_CLIENT_SECRET=${PAPERLESS_CLIENT_SECRET}
-+      - PAPERLESS_OIDC_RP_PROVIDER_URL=https://auth.mrx8086.com/realms/office-automation
-+      - PAPERLESS_OIDC_RP_USERNAME_CLAIM=preferred_username
-+      - PAPERLESS_OIDC_RP_NAME_CLAIM=name
-+      - PAPERLESS_OIDC_RP_EMAIL_CLAIM=email
-+      - PAPERLESS_OIDC_RP_RENEW_TOKEN_BEFORE_EXPIRY=true
-+      - PAPERLESS_OIDC_RP_VERIFY_SSL=false
-+    depends_on:
-+      - paperless-db
-+    networks:
-+      - paperless-network
-+    extra_hosts:
-+      - "auth.mrx8086.com:172.23.171.133"
-+    healthcheck:
-+      test: ["CMD", "curl", "-f", "http://localhost:8000/"]
-+      interval: 30s
-+      timeout: 10s
-+      retries: 3
-+
-+  paperless-db:
-+    image: postgres:15
-+    container_name: paperless-db
-+    restart: unless-stopped
-+    environment:
-+      POSTGRES_USER: ${PAPERLESS_DB_USER}
-+      POSTGRES_PASSWORD: ${PAPERLESS_DB_PASSWORD}
-+      POSTGRES_DB: paperless
-+    volumes:
-+      - ../data/paperless-db:/var/lib/postgresql/data
-+    networks:
-+      - paperless-network
-+    healthcheck:
-+      test: ["CMD-SHELL", "pg_isready -U ${PAPERLESS_DB_USER} -d paperless"]
-+      interval: 10s
-+      timeout: 5s
-+      retries: 5
-+
- networks:
-   keycloak-network:
-     driver: bridge
-   nextcloud-network:
-+    driver: bridge
-+  paperless-network:
-     driver: bridge
-\ No newline at end of file
-diff --git a/git_diff.txt b/git_diff.txt
-index bfb2d94..2f87fcb 100644
---- a/git_diff.txt
-+++ b/git_diff.txt
-@@ -1,823 +0,0 @@
--Vielen Dank für die Bereitstellung der Dateien. Ich habe alle notwendigen Informationen, um die Dokumentation zu erstellen. Hier ist der Inhalt der finalen Dokumentationsdateien:
--
--```markdown
--# Automatisiertes Büro 2.0 - Projektdokumentation
--
--## Inhaltsverzeichnis
--1.  [Projektübersicht](#projektübersicht)
--2.  [Systemarchitektur](#systemarchitektur)
--3.  [Entwicklungsumgebung](#entwicklungsumgebung)
--4.  [Konfigurationen](#konfigurationen)
--5.  [Installationsanleitung](#installationsanleitung)
--6.  [Workflows](#workflows)
--7.  [Sicherheitskonzept](#sicherheitskonzept)
--8.  [Wartung und Monitoring](#wartung-und-monitoring)
--9.  [Troubleshooting](#troubleshooting)
--
--## Projektübersicht
--
--### Projektziele
--- Vollständige Automatisierung administrativer und kaufmännischer Prozesse
--- Kostensenkung durch lokale Open-Source-Lösungen
--- Unabhängigkeit von externen Diensten
--- Zentralisierte Verwaltung aller Geschäftsprozesse
--
--### Projektumfang
--- Integration aller Kommunikationskanäle
--- Automatisierte Dokumentenverarbeitung
--- Prozessautomatisierung
--- Zentrale Authentifizierung
--- KI-gestützte Korrespondenz
--
--#### Keycloak-Rolle
--Keycloak spielt eine zentrale Rolle als Authentifizierungsstelle. Es stellt die Single-Sign-On (SSO) Funktionalität für alle Dienste der zentralen Plattform bereit und sichert somit den Zugriff auf die verschiedenen Anwendungen.
--
--## Systemarchitektur
--
--#### Architekturübersicht
--
--```mermaid
--graph TB
--    subgraph Eingangssysteme
--        Email[E-Mail]
--        WhatsApp[WhatsApp]
--        Post[Physische Post]
--        Teams[Teams/Webex]
--    end
--
--    subgraph Zentrale_Plattform
--        NC[Nextcloud - Dokumentenverwaltung]
--        PL[Paperless - Dokumentenmanagement]
--        NR[Node-RED - Prozessautomatisierung]
--        KC[Keycloak - SSO]
--        OUI[OpenWebUI - KI-Korrespondenz]
--		KI[Kimai - Zeiterfassung]
--    end
--
--    subgraph Monitoring_Analytics
--        ELK[ELK Stack - Logging & Analyse]
--    end
--
--    subgraph Geschäftsprozesse
--        TP[Task-Priorisierung]
--        subgraph Finanzen
--            RE[Rechnungserstellung]
--            ZA[Zahlungsabwicklung]
--            BA[Banken-API]
--        end
--        subgraph Verwaltung
--            KV[Kundenverwaltung]
--            ZE[Zeiterfassung]
--            DO[Dokumentenarchiv]
--        end
--    end
--
--    Email --> NC
--    WhatsApp --> NC
--    Post --> PL
--    Teams --> NC
--
--    NC --> NR
--    PL --> NR
--    NR --> TP
--	KI --> TP
--
--    TP --> RE
--    TP --> ZA
--    TP --> KV
--    TP --> ZE
--    TP --> DO
--
--    ZA <--> BA
--
--    KC -.->|Authentifizierung| NC
--    KC -.->|Authentifizierung| PL
--    KC -.->|Authentifizierung| NR
--    KC -.->|Authentifizierung| OUI
--	KC -.->|Authentifizierung| KI
--
--    NR --> ELK
--    OUI --> NR
--
--    classDef container fill:#e1f5fe,stroke:#01579b
--    classDef process fill:#e8f5e9,stroke:#2e7d32
--    classDef auth fill:#fff3e0,stroke:#ef6c00
--    classDef monitoring fill:#fce4ec,stroke:#c2185b
--
--    class NC,PL,NR,KI container
--    class TP,RE,ZA,KV,ZE,DO process
--    class KC auth
--    class ELK monitoring
--```
--
--#### Beschreibung der Architekturkomponenten
--
--Die Systemarchitektur ist in vier Hauptbereiche gegliedert:
--
--1.  **Eingangssysteme:**
--    - Erfassen verschiedene Kommunikationskanäle zentral.
--    - Sorgen für eine einheitliche Weiterverarbeitung aller Eingänge.
--
--2.  **Zentrale Plattform:**
--    - **Nextcloud (NC):** Dient als zentraler Hub für die Dateiverwaltung und Kollaboration.
--    - **Paperless (PL):** Zuständig für das Dokumentenmanagement und die optische Zeichenerkennung (OCR).
--    - **Node-RED (NR):** Automatisierung von Workflows und Geschäftsprozessen.
--    - **Keycloak (KC):** Bereitstellung von Single-Sign-On (SSO) und Identitätsmanagement.
--        - Keycloak wird als zentrale Authentifizierungsstelle für alle Dienste der zentralen Plattform verwendet, wodurch ein sicherer und zentralisierter Zugriff gewährleistet wird.
--    - **OpenWebUI (OUI):** KI-gestützte Kommunikation und Integration in die Workflow-Automatisierung
--    - **Kimai (KI):** Zeiterfassungslösung zur Verwaltung von Arbeitszeiten und Projekten.
--
--3.  **Geschäftsprozesse:**
--    - Automatisierte Task-Priorisierung (TP) für eine effiziente Aufgabenverteilung.
--    - Integrierte Finanzprozesse mit Bankenanbindung (RE, ZA, BA).
--    - Zentralisierte Verwaltungsprozesse (KV, ZE, DO).
--
--4.  **Monitoring & Analytics:**
--    - **ELK Stack (ELK):** Ermöglicht umfassendes Logging und Analyse in Echtzeit zur Überwachung aller Systeme.
--
--#### Containerstruktur
--- Docker als Containerisierungsplattform
--- Microservices-Architektur
--- Interne Netzwerkkonfiguration
--
--### Komponenten
--#### Nextcloud
--- Funktion: Zentrale Dateiverwaltung und Kollaboration
--- Version: `[VERSION]`
--- Besondere Konfigurationen:
--  - [Wird ergänzt]
--
--#### Paperless
--- Funktion: Dokumentenmanagement und OCR
--- Version: `[VERSION]`
--- Besondere Konfigurationen:
--  - [Wird ergänzt]
--
--#### Keycloak
--- Funktion: Single-Sign-On und Identitätsmanagement
--- Version: `[VERSION]`
--- Besondere Konfigurationen:
--  - [Wird ergänzt]
--
--#### Node-RED
--- Funktion: Workflow-Automatisierung
--- Version: `[VERSION]`
--- Implementierte Flows:
--  - [Wird ergänzt]
--
--#### ELK Stack
--- Funktion: Logging und Monitoring
--- Version: `[VERSION]`
--- Besondere Konfigurationen:
--  - [Wird ergänzt]
--
--#### OpenWebUI
--- Funktion: KI-gestützte Kommunikation
--- Version: `[VERSION]`
--- Integrationen:
--  - [Wird ergänzt]
--
--#### Kimai
--- Funktion: Zeiterfassung
--- Version: `[VERSION]`
--- Besondere Konfigurationen:
--  - [Wird ergänzt]
--
--## Entwicklungsumgebung
--
--### Systemvoraussetzungen
--- Windows mit WSL (Windows Subsystem for Linux)
--- Visual Studio Code
--- Docker Desktop
--- NGINX Proxy Manager (läuft in WSL)
--
--#### Domain-Konfiguration
--Die Entwicklungsumgebung nutzt die Domain `mrx8086.com` mit verschiedenen Subdomains für die einzelnen Services. Diese werden lokal über die Windows-Hosts-Datei (`C:\Windows\System32\drivers\etc\hosts`) aufgelöst.
--
--```plaintext
--# Development Environment Host Entries
--127.0.0.1 mrx8086.com
--127.0.0.1 proxy.mrx8086.com     # NGINX Proxy Manager
--172.23.171.133 auth.mrx8086.com # Keycloak
--127.0.0.1 cloud.mrx8086.com     # Nextcloud
--127.0.0.1 docs.mrx8086.com      # Paperless
--127.0.0.1 time.mrx8086.com      # Kimai
--127.0.0.1 automate.mrx8086.com  # n8n
--```
--
--#### Service-Übersicht
--| Subdomain | Service | Beschreibung |
--|-----------|---------|--------------|
--| proxy.mrx8086.com | NGINX Proxy Manager | Reverse Proxy und SSL-Management (lokal in der Development Umgebung) |
--| auth.mrx8086.com | Keycloak | Zentrale Authentifizierung |
--| cloud.mrx8086.com | Nextcloud | Dokumentenverwaltung |
--| docs.mrx8086.com | Paperless | Dokumentenmanagement |
--| time.mrx8086.com | Kimai | Zeiterfassung |
--| automate.mrx8086.com | n8n | Workflow-Automatisierung |
--
--#### WSL-Konfiguration
--- NGINX Proxy Manager läuft in WSL
--- IP-Adresse des WSL-Systems: 172.23.171.133 (Beispiel, kann sich ändern)
--- Alle Docker-Container werden innerhalb von WSL betrieben
--
--#### `setup_realm.js`
--Dieses Skript wird verwendet, um den Keycloak-Realm, die zugehörigen Clients und Testbenutzer automatisiert zu erstellen. Es verwendet eine `.env`-Datei zur Konfiguration.
--
--#### Verwendung von .env
--Die Konfigurationen für das setup_realm.js Script werden in einer .env Datei gespeichert. Die benötigten Umgebungsvariablen sind unten aufgelistet:
--- `KEYCLOAK_URL`: Die URL zum Keycloak Server (z.B. https://auth.mrx8086.com)
--- `KEYCLOAK_ADMIN_USER`: Der Benutzername des Keycloak Administrators (z.B. admin).
--- `KEYCLOAK_ADMIN_PASSWORD`: Das Passwort des Keycloak Administrators.
--- `NEXTCLOUD_CLIENT_ID`: Die Client ID für Nextcloud. (z.B. nextcloud)
--- `PAPERLESS_CLIENT_ID`: Die Client ID für Paperless (z.B. paperless).
--- `NODERED_CLIENT_ID`: Die Client ID für Node-RED (z.B. nodered).
--- `TESTADMIN_PASSWORD`: Das Passwort für den Testadmin User.
--- `TESTUSER_PASSWORD`: Das Passwort für den Testuser User.
--- `TESTSERVICEUSER_PASSWORD`: Das Passwort für den Testserviceuser User.
--- `KEYCLOAK_NEXTCLOUD_CLIENT_SECRET`: Das Client Secret für Nextcloud.
--- `NEXTCLOUD_URL`: Die URL zur Nextcloud Instanz (z.B. https://cloud.mrx8086.com).
--
--#### NGINX-Konfigurationen
--Für jeden Service existiert eine dedizierte NGINX-Konfiguration. In der Development Umgebung wird der **NGINX Proxy Manager** verwendet. Für **Staging** und **Production** werden die entsprechenden NGINX Konfigurationsdateien in `/config/nginx` abgelegt.
--
--## Konfigurationen
--
--### Netzwerkkonfiguration
--- Interne Netzwerkstruktur (noch zu definieren)
--- Reverse Proxy Konfiguration über den Nginx Proxy Manager in der Development Umgebung, und Nginx Server Config in Staging und Production.
--- SSL/TLS-Setup: Selbsignierte Zertifikate in der Development Umgebung, Letsencrypt in Staging und Production.
--- Konfigurationsdateien für NGINX Server Config sind unter `/config/nginx/sites-available/` zu finden.
--- Für die Entwicklungsumgebung werden die Konfigurationen über den Nginx Proxy Manager konfiguriert.
--- Die SSL Zertifikate für die Development Umgebung werden als selbsignierte Zertifikate generiert und in `/config/nginx/ssl/mrx8086.com/` abgelegt.
--- In der Staging und Production Umgebung werden die Zertifikate über Let's Encrypt oder eine andere Zertifizierungsstelle verwaltet.
--
--#### Umgebungsvariablen
--Es werden zwei `.env`-Dateien verwendet:
--Eine im `docker/` Verzeichnis für die Docker-Konfiguration und eine im `scripts/setup/keycloak/` für das `setup_realm.js`-Skript.
--
--**`docker/.env`:**
--```env
--# Generated on 2024-12-12_18-12-36
--# Keycloak Admin
--KEYCLOAK_ADMIN_PASSWORD=9aD5Fddh457QqmvQqr6Rb8bu
--
--# Keycloak Database
--KC_DB_USERNAME=keycloak
--KC_DB_PASSWORD=p47616y763z101f3
--```
--
--**`scripts/setup/keycloak/.env`:**
--```env
--KEYCLOAK_URL=https://auth.mrx8086.com
--KEYCLOAK_ADMIN_USER=admin
--KEYCLOAK_ADMIN_PASSWORD=9aD5Fddh457QqmvQqr6Rb8bu
--NEXTCLOUD_CLIENT_ID=nextcloud
--PAPERLESS_CLIENT_ID=paperless
--NODERED_CLIENT_ID=nodered
--TESTADMIN_PASSWORD=TestAdminPwd
--TESTUSER_PASSWORD=TestUserPwd
--TESTSERVICEUSER_PASSWORD=TestServiceUserPwd
--KEYCLOAK_NEXTCLOUD_CLIENT_SECRET=OSbJ08zyjBWChwBR7S6c1q4sU0d8zvEK
--NEXTCLOUD_URL=https://cloud.mrx8086.com
--```
--
--Die Passwörter in der `.env` Datei im `scripts/setup/keycloak` werden vom `setup_environment.sh` Skript generiert.
--
--#### Keycloak-Konfiguration
--
--Keycloak ist als zentrale Authentifizierungsstelle konfiguriert. Die Realm-Konfiguration, Clients und Benutzer werden über das `setup_realm.js` Skript definiert, die Konfiguration wird über die `.env` Datei im `/scripts/setup/keycloak` Verzeichnis angepasst.
--
--##### Realm-Konfiguration
--- **Realm-Name:** `office-automation`
--- **Anzeige Name:** `Office Automation`
--- **SSL ist erforderlich**: `external`
--- **Registrierung ist nicht erlaubt**: `false`
--- **Login mit Email ist erlaubt**: `true`
--- **Doppelte Email ist nicht erlaubt**: `false`
--- **Passwort Reset ist erlaubt**: `true`
--- **Username Bearbeitung ist nicht erlaubt**: `false`
--- **Brute Force Schutz ist aktiviert**: `true`
--- **Permanente Sperrung ist nicht aktiviert**: `false`
--- **Standard Signature Algorithm**: `RS256`
--- **WebAuthn Policy Signatur Algorithmen**: `ES256`
--- **WebAuthn Policy Attestation Conveyance Preference**: `none`
--- **WebAuthn Policy Authenticator Attachment**: `cross-platform`
--- **WebAuthn Policy Require Resident Key**: `not specified`
--- **WebAuthn Policy User Verification Requirement**: `preferred`
--- **WebAuthn Policy Create Timeout**: `0`
--- **WebAuthn Policy Avoid Same Authenticator Register**: `false`
--- **Default Default Client Scopes**: `email`, `profile`, `roles`, `web-origins`
--- **Default Optional Client Scopes**: `address`, `phone`, `offline_access`, `microprofile-jwt`
--
--##### Clients
--Die folgenden Clients werden über das Skript konfiguriert:
--- **Nextcloud:**
--  -   **Client ID:** `nextcloud`
--  -   **Name:** `Nextcloud`
--   -   **Redirect URIs:**
--        - `https://cloud.mrx8086.com/apps/sociallogin/custom_oidc/keycloak`
--        - `https://cloud.mrx8086.com/apps/user_oidc/code`
--    -   **Post Logout Redirect URIs:** `https://cloud.mrx8086.com/*`
--- **Paperless:**
--  -   **Client ID:** `paperless`
--  -   **Name:** `Paperless`
--  -   **Redirect URIs:** `https://docs.mrx8086.com/*`
--- **Node-RED:**
--  -   **Client ID:** `nodered`
--  -   **Name:** `Node-RED`
--  -   **Redirect URIs:** `https://automate.mrx8086.com/*`
--
--##### Gruppen
--- **nextcloud-admins:** Gruppe für Nextcloud Benutzer mit Admin Rechten.
--- **nextcloud-users:** Gruppe für reguläre Nextcloud Benutzer.
--- **nextcloud-youpi:** Gruppe für Nextcloud Youpi Benutzer.
--- **nextcloud-service:** Gruppe für Nextcloud Service Benutzer.
--
--##### Benutzer
--- **testadmin:**
--  - Benutzername: `testadmin`
--  - Passwort: wird entweder aus der Umgebungsvariable `TESTADMIN_PASSWORD` gelesen oder standardmäßig `initial123!` gesetzt.
--  - Gruppen: `nextcloud-admins`,`nextcloud-users`
--- **testuser:**
--  - Benutzername: `testuser`
--  - Passwort: wird entweder aus der Umgebungsvariable `TESTUSER_PASSWORD` gelesen oder standardmäßig `initial123!` gesetzt.
--  - Gruppen: `nextcloud-users`,`nextcloud-youpi`
--- **testserviceuser:**
--    - Benutzername: `testserviceuser`
--    - Passwort: wird entweder aus der Umgebungsvariable `TESTSERVICEUSER_PASSWORD` gelesen oder standardmäßig `initial123!` gesetzt.
--    - Gruppen: `nextcloud-service`
--
--#### SSL/TLS-Setup
---   **Entwicklungsumgebung (`dev`):** Für lokale Entwicklung werden selbsignierte SSL-Zertifikate verwendet.
---   **Staging/Produktionsumgebung (`staging`, `production`):** Hier werden SSL-Zertifikate über Let's Encrypt oder eine ähnliche Zertifizierungsstelle verwaltet.
--
--## Installationsanleitung
--#### Voraussetzungen
--- Docker Version: `[VERSION]`
--- Minimal Systemanforderungen:
--  - CPU: `[ANFORDERUNG]`
--  - RAM: `[ANFORDERUNG]`
--  - Speicher: `[ANFORDERUNG]`
--- Für die Ausführung des `setup_realm.js` Skriptes wird `Node.js` benötigt
--- Die genaue Version von `Node.js` wird zu einem späteren Zeitpunkt dokumentiert.
--- Vor dem starten müssen die Passwörter generiert und die `.env` Dateien erstellt werden. Hierzu wird das `setup_environment.sh` verwendet. Dieses Skript sollte **nicht in der Produktionsumgebung** verwendet werden.
--- Das Skript erstellt die benötigten Verzeichnisse, generiert zufällige Passwörter, speichert diese verschlüsselt ab, und erstellt die benötigten `.env` Dateien.
--- Für die Ausführung des `setup_realm.js` Skriptes wird `Node.js` und `npm` benötigt
--- Die benötigten Node Module `dotenv` und `axios` können mit `npm install dotenv axios` installiert werden.
--- Die `.env` Dateien werden im `/docker` und im `/scripts/setup/keycloak` Verzeichnis abgelegt.
--
--#### Keycloak-Installation
--1.  Führe das `setup_environment.sh` Skript aus, um die .env Dateien, die Passwörter zu generieren und die Credentials zu verschlüsseln.
--2.  Kopiere die `docker-compose.yml` Datei in das `docker/` Verzeichnis.
--3.  Kopiere die `.env` Datei in das `docker/` Verzeichnis.
--4.  Führe `docker-compose up -d` im `docker/` Verzeichnis aus um Keycloak zu starten.
--
--#### Ausführen von `setup_realm.js`
--1.  Stelle sicher, dass Node.js und npm installiert sind.
--2.  Wechsle in das `/scripts/setup/keycloak` Verzeichnis.
--3.  Installiere die benötigten npm Pakete mit `npm install dotenv axios`.
--4.  Führe das Skript mit `node setup_realm.js` aus. Dies erstellt den Keycloak-Realm, die Clients und die Testbenutzer.
--    * Das Script befindet sich im `/scripts/setup/keycloak` Verzeichnis
--
--## Workflows
--#### Dokumentenverarbeitung
--- Eingangsverarbeitung
--- OCR-Prozess
--- Kategorisierung
--- Archivierung
--
--#### Geschäftsprozesse
--- Rechnungsstellung
--- Zahlungsabwicklung
--- Kundenmanagement
--- Zeiterfassung
--
--#### Authentifizierungsflow
--Die Authentifizierung der Benutzer für alle Dienste wird über Keycloak abgewickelt. Details zu den Authentifizierungsabläufen werden in einem späteren Schritt dokumentiert.
--
--## Sicherheitskonzept
--#### Zugriffsmanagement
--- Rollenkonzept (wird in späteren schritten dokumentiert)
--- Berechtigungsmatrix (wird in späteren schritten dokumentiert)
--- Authentifizierungsflows (siehe Workflows)
--- Keycloak wird als zentrale Authentifizierungsstelle verwendet, Passwortrichtlinien werden in Keycloak definiert.
--
--#### Datensicherheit
--- Verschlüsselung (wird in späteren schritten dokumentiert)
--- Backup-Strategie (wird in späteren schritten dokumentiert)
--- Notfallwiederherstellung (wird in späteren schritten dokumentiert)
--
--## Wartung und Monitoring
--#### Regelmäßige Wartungsaufgaben
--- Backup-Überprüfung
--- Updates
--- Performance-Monitoring
--
--Details zur Wartung und dem Monitoring werden in späteren Schritten dokumentiert.
--
--## Troubleshooting
--#### Bekannte Probleme
--- [Wird ergänzt mit auftretenden Problemen]
--
--#### Debugging
--- Log-Analyse
--- Fehlerbehandlung
--- Support-Prozesse
--Details zu bekannten Problemen und zur Fehlerbehandlung werden in späteren Schritten dokumentiert.
--```
--```markdown
--# Automated Office 2.0 - Current Project State
--
--## Overview
--Project to automate all administrative and commercial processes within the company, using open-source solutions.
--
--## Current Implementation Status
--
--### 1. Project Structure
--- Basic directory structure created
--- Ansible roles established (common, docker, nginx, services)
--- Configuration directories set up for all services
--- Documentation structure established
--
--### 2. Environment Setup
--- Development environment using WSL
--- NGINX running in WSL for development using NGINX Proxy Manager
--- Docker environment being set up
--- SSL certificates in place for development (self-signed)
--- Staging and Production environment will use NGINX Server Configs and letsencrypt SSL certificates
--
--### 3. Service Status
--
--#### Keycloak (auth.mrx8086.com)
--- NGINX configuration complete
--- Docker setup complete
--- Keycloak is running behind a reverse proxy
--- Implemented setup_realm.js script for automated realm, client and user setup
--- SSL certificates configured (self-signed)
--- `setup_realm.js` configures the `office-automation` realm, `nextcloud`, `paperless`, and `nodered` clients.
--- Test users `testadmin`, `testuser` and `testserviceuser` are also created.
--- Client Scopes for `openid`, `profile` and `groups-nextcloud` are added to the nextcloud client.
--- Groups `nextcloud-admins`, `nextcloud-users`, `nextcloud-youpi` and `nextcloud-service` are created.
--
--#### Nextcloud (cloud.mrx8086.com)
--- NGINX configuration complete
--- Docker setup complete
--- SSL certificates configured
--- Nextcloud is configured to use Keycloak for authentication
--
--#### Paperless (docs.mrx8086.com)
--- NGINX configuration complete
--- Docker setup pending
--- SSL certificates configured
--
--#### Node-RED (automate.mrx8086.com)
--- NGINX configuration complete
--- Docker setup pending
--- SSL certificates configured
--- Chosen over n8n for better open-source compatibility
--
--### 4. Security
--- Automated password generation implemented
--- Encrypted credentials storage system in place
--- SSL certificates managed and deployed
--
--### 5. Development Decisions
--- Using WSL for development environment
--- NGINX running directly in WSL for development
--- Docker containers for all services
--- Focusing on completely open-source solutions
--- Development environment uses Nginx Proxy Manager
--- Staging and Production will use Nginx Server Config files
--
--## Next Steps
--1. Complete Paperless and Node-RED docker setup
--2. Test Paperless and Node-RED authentication against Keycloak
--3. Proceed with remaining service deployments
--4. Setup Letsencrypt SSL Certificates in the Staging Environment
--
--## Important Files Location
--- NGINX configs: /config/nginx/sites-available/
--- SSL certificates: /config/nginx/ssl/mrx8086.com/
--- Docker compose: /docker/docker-compose.yml
--- Environment variables: /config/.env
--- Encrypted credentials: /config/credentials/
--- Keycloak setup script: /scripts/install/setup_realm.js
--
--## Development Environment
--- Domain: mrx8086.com
--- SSL certificates in place (self-signed)
--- NGINX running in WSL
--- Docker running in WSL
--```
--```markdown
--# Ansible Setup Documentation
--
--## Overview
--Ansible wird für das automatisierte Deployment des Automated Office Systems verwendet.
--
--## Roles Structure
--
--### Common Role
--- Basis-Systemkonfiguration
--- Sicherheitseinstellungen (fail2ban, UFW)
--- Grundlegende Systempakete
--
--```yaml
--# Standardvariablen
--timezone: "Europe/Berlin"
--fail2ban_bantime: 600
--fail2ban_findtime: 600
--fail2ban_maxretry: 3
--```
--
--### Docker Role
--- Docker Installation und Konfiguration
--- Docker Compose Setup
--- Docker Netzwerk-Konfiguration
--
--```yaml
--# Docker Standardvariablen
--docker_version: "latest"
--docker_compose_version: "2.21.0"
--docker_users: ["{{ ansible_user }}"]
--```
--
--### NGINX Role
--- NGINX Installation
--- SSL/TLS Setup
--- Virtual Host Konfiguration
--
--```yaml
--# NGINX Standardvariablen
--nginx_worker_processes: auto
--nginx_worker_connections: 1024
--nginx_client_max_body_size: "100M"
--```
--
--### Services Role
--- Deployment der Docker-Container
--- Service-spezifische Konfigurationen
--- Datenpersistenz-Setup
--
--## Inventory Structure
--```plaintext
--inventory/
--├── production/
--└── staging/
--    └── hosts
--```
--
--## Variables
--```yaml
--# vars/defaults/main.yml
--base_domain: "example.com"
--ssl_email: "admin@example.com"
--
--services:
--  keycloak: true
--  nextcloud: true
--  paperless: true
--  nodered: true
--```
--```yaml
--# ansible/roles/common/defaults/main.yml
-----
--# System
--timezone: "Europe/Berlin"
--
--# Security
--fail2ban_bantime: 600
--fail2ban_findtime: 600
--fail2ban_maxretry: 3
--
--# Firewall ports to open
--ufw_allowed_ports:
--  - { port: 22, proto: tcp }  # SSH
--  - { port: 80, proto: tcp }  # HTTP
--  - { port: 443, proto: tcp } # HTTPS
--```
--```yaml
--# ansible/roles/docker/defaults/main.yml
-----
--# Docker Standardvariablen
--docker_version: "latest"
--docker_compose_version: "2.21.0"
--docker_users: ["{{ ansible_user }}"]
--```
--```yaml
--# ansible/roles/nginx/defaults/main.yml
-----
--# NGINX Standardvariablen
--nginx_worker_processes: auto
--nginx_worker_connections: 1024
--nginx_client_max_body_size: "100M"
--```
--```yaml
--# ansible/vars/defaults/main.yml
-----
--# Domain-Konfiguration
--base_domain: "example.com"
--ssl_email: "admin@example.com"
--
--# Aktivierte Services
--services:
--  keycloak: true
--  nextcloud: true
--  paperless: true
--  nodered: true
--
--# Ports
--keycloak_port: 8080
--nextcloud_port: 8081
--paperless_port: 8000
--nodered_port: 1880
--
--# Docker-Konfiguration
--docker_compose_version: "2.21.0"
--```
--## Deployment Flow
--1. Common Role: Systemvorbereitung
--2. Docker Role: Container-Runtime
--3. NGINX Role: Reverse Proxy
--4. Services Role: Anwendungen
--
--## Wichtige Befehle
--```bash
--# Staging Deployment
--ansible-playbook -i inventory/staging site.yml
--
--# Production Deployment
--ansible-playbook -i inventory/production site.yml
--```
--
--## Sicherheitsaspekte
--- Automatische Passwortverwaltung
--- SSL/TLS-Konfiguration
--- Firewall-Einstellungen
--- Fail2ban-Integration
--
--## Entwicklungshinweise
--- Lokales Testing über WSL
--- Staging-Umgebung für Tests
--- Produktionsumgebung für finale Deployments
--
--## Updates und Wartung
--- Regelmäßige Updates über Ansible
--- Backup-Integration
--- Monitoring-Setup
--```
--```bash
--#!/bin/bash
--set -e
--
--# Ensure we're in the project root directory
--PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
--
--# Define directories relative to project root
--CREDENTIALS_DIR="${PROJECT_ROOT}/config/credentials"
--DOCKER_DIR="${PROJECT_ROOT}/docker"
--KEYCLOAK_SETUP_DIR="${PROJECT_ROOT}/scripts/setup/keycloak"
--ANSIBLE_PLAYBOOK="${PROJECT_ROOT}/ansible/site.yml"
--ANSIBLE_INVENTORY="${PROJECT_ROOT}/ansible/inventory/staging/hosts"
--NEXTCLOUD_DATA_DIR="${PROJECT_ROOT}/data/nextcloud/data"
--TEMP_FILE=$(mktemp)
--KEYCLOAK_DB_DIR="${PROJECT_ROOT}/data/keycloak-db"
--
--# Create necessary directories
--sudo mkdir -p "${CREDENTIALS_DIR}"
--sudo mkdir -p "${DOCKER_DIR}"
--sudo mkdir -p "${KEYCLOAK_SETUP_DIR}"
--
--# Initialize password variables
--KEYCLOAK_ADMIN_PASSWORD=""
--KC_DB_PASSWORD=""
--TESTADMIN_PASSWORD=""
--TESTUSER_PASSWORD=""
--TESTSERVICEUSER_PASSWORD=""
--KEYCLOAK_NEXTCLOUD_CLIENT_SECRET=""
--
--# Function to read a password from a .env file
--read_password_from_env() {
--    local env_file="$1"
--    local variable_name="$2"
--    if [ -f "$env_file" ]; then
--        grep "^${variable_name}=" "$env_file" | cut -d '=' -f2
--    fi
--}
--
--# Function to generate secure passwords
--generate_password() {
--    openssl rand -base64 32
--}
--
--# Function to generate password if empty
--generate_password_if_empty() {
--    local variable_name="$1"
--    eval "local value=\$$variable_name"
--    if [ -z "$value" ]; then
--        eval "$variable_name=\"$(generate_password)\""
--        echo ">>> Generiertes Passwort für: $variable_name"
--    fi
--}
--
--# Function to create .env file
--create_env_file() {
--    local env_file="$1"
--    local content="$2"
--    if [ ! -f "$env_file" ]; then
--        echo "$content" > "$env_file"
--        echo ">>> .env file created: $env_file"
--    else
--        echo ">>> .env file already exists: $env_file"
--    fi
--}
--
--echo ">>> Überprüfe bestehende .env Dateien und lese Passwörter..."
--
--# Try reading passwords from existing .env files
--if [ -f "$DOCKER_DIR/.env" ]; then
--    KC_DB_PASSWORD=$(read_password_from_env "$DOCKER_DIR/.env" "KC_DB_PASSWORD")
--    KEYCLOAK_ADMIN_PASSWORD=$(read_password_from_env "$DOCKER_DIR/.env" "KEYCLOAK_ADMIN_PASSWORD")
--fi
--
--if [ -f "$KEYCLOAK_SETUP_DIR/.env" ]; then
--    KEYCLOAK_ADMIN_PASSWORD=$(read_password_from_env "$KEYCLOAK_SETUP_DIR/.env" "KEYCLOAK_ADMIN_PASSWORD") # Überschreibt ggf. den Wert aus docker/.env
--    TESTADMIN_PASSWORD=$(read_password_from_env "$KEYCLOAK_SETUP_DIR/.env" "TESTADMIN_PASSWORD")
--    TESTUSER_PASSWORD=$(read_password_from_env "$KEYCLOAK_SETUP_DIR/.env" "TESTUSER_PASSWORD")
--    TESTSERVICEUSER_PASSWORD=$(read_password_from_env "$KEYCLOAK_SETUP_DIR/.env" "TESTSERVICEUSER_PASSWORD")
--    KEYCLOAK_NEXTCLOUD_CLIENT_SECRET=$(read_password_from_env "$KEYCLOAK_SETUP_DIR/.env" "KEYCLOAK_NEXTCLOUD_CLIENT_SECRET")
--fi
--
--echo ">>> Generiere neue Passwörter für fehlende Werte..."
--
--# Generate passwords if they are still empty
--generate_password_if_empty KEYCLOAK_ADMIN_PASSWORD
--generate_password_if_empty KC_DB_PASSWORD
--generate_password_if_empty TESTADMIN_PASSWORD
--generate_password_if_empty TESTUSER_PASSWORD
--generate_password_if_empty TESTSERVICEUSER_PASSWORD
--generate_password_if_empty KEYCLOAK_NEXTCLOUD_CLIENT_SECRET
--
--# Date for documentation
--SETUP_DATE=$(date '+%Y-%m-%d_%H-%M-%S')
--
--# Create credentials content
--CREDENTIALS_CONTENT=$(cat <<EOL
--Setup Date: ${SETUP_DATE}
--
--Keycloak Admin Credentials:
--Username: admin
--Password: ${KEYCLOAK_ADMIN_PASSWORD}
--
--Keycloak Database Credentials:
--Username: keycloak
--Password: ${KC_DB_PASSWORD}
--
--Test User Credentials:
--Admin Password: ${TESTADMIN_PASSWORD}
--User Password: ${TESTUSER_PASSWORD}
--Service User Password: ${TESTSERVICEUSER_PASSWORD}
--Nextcloud Client Secret: ${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}
--
--EOL
--)
--
--# Store credentials hash
--CREDENTIALS_HASH=$(echo "$CREDENTIALS_CONTENT" | sha256sum | awk '{print $1}')
--echo "$CREDENTIALS_HASH" > "${CREDENTIALS_DIR}/credentials_hash.txt"
--echo ">>> Credentials hash stored in: ${CREDENTIALS_DIR}/credentials_hash.txt"
--
--# Set GPG PASSPHRASE
--export GPG_PASSPHRASE=$(generate_password)
--# Set GPG agent environment variable
--export GPG_TTY=$(tty)
--
--echo ">>> Trying openssl encryption first"
--# Alternative Verschlüsselung mit Openssl
--echo "$CREDENTIALS_CONTENT" > "$TEMP_FILE"
--          if openssl enc -aes-256-cbc -pbkdf2 -salt -in "$TEMP_FILE" -out "${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt.enc" -k "$GPG_PASSPHRASE" ; then
--             echo ">>> Credentials encrypted successfully using openssl"
--             mv  "${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt.enc" "${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt.gpg"
--        else
--           echo ">>> Openssl encryption failed, trying gpg"
--
--          # Attempt to kill existing gpg agent
--          gpgconf --kill gpg-agent 2>/dev/null
--          echo ">>> Attempting to manually start gpg-agent with pinentry-curses"
--          gpg-agent --daemon --pinentry-program /usr/bin/pinentry-curses
--          gpg-connect-agent /bye 2>/dev/null
--          eval $(gpg-agent --daemon)
--          gpg-connect-agent updatestartuptty /bye 2>/dev/null
--
--          # Attempt to encrypt credentials using GPG with error handling
--          if echo "$CREDENTIALS_CONTENT" | gpg --symmetric --cipher-algo AES256 -vvv -o "${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt.gpg" ; then
--                echo ">>> Credentials encrypted successfully using gpg."
--          else
--             echo
--
--
--
--
--
--
--             ```
-\ No newline at end of file

+ 281 - 0
scripts/setup/paperless/check_keycloak_client.js

@@ -0,0 +1,281 @@
+import dotenv from 'dotenv';
+import axios from 'axios';
+
+// Load environment variables
+dotenv.config();
+
+// Configuration constants
+const KEYCLOAK_URL = process.env.KEYCLOAK_URL || 'https://auth.mrx8086.com';
+const ADMIN_USERNAME = process.env.KEYCLOAK_ADMIN_USER;
+const ADMIN_PASSWORD = process.env.KEYCLOAK_ADMIN_PASSWORD;
+const REALM_NAME = 'office-automation';
+
+// Client ID for Paperless
+const PAPERLESS_CLIENT_ID = process.env.PAPERLESS_CLIENT_ID || 'paperless';
+
+// Helper function for API error handling
+const handleAxiosError = (error, operation, config, response) => {
+    console.error(`Error during ${operation}:`);
+    if (config) {
+      console.error('Request:', {
+          method: config.method,
+          url: config.url,
+          headers: config.headers,
+          data: config.data,
+      });
+    }
+    if (error.response) {
+        console.error('Response:', {
+            status: error.response.status,
+            data: error.response.data
+        });
+    } else {
+        console.error('Error Message:', error.message);
+    }
+    throw error;
+};
+
+// Get Admin Token
+async function getAdminToken() {
+    console.log('Getting admin token...');
+    try {
+        const response = await axios.post(
+            `${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token`,
+            new URLSearchParams({
+                'client_id': 'admin-cli',
+                'username': ADMIN_USERNAME,
+                'password': ADMIN_PASSWORD,
+                'grant_type': 'password'
+            }),
+            {
+                headers: {
+                    'Content-Type': 'application/x-www-form-urlencoded'
+                }
+            }
+        );
+        console.log('Admin token received.');
+        return response.data.access_token;
+    } catch (error) {
+        handleAxiosError(error, 'getting admin token');
+    }
+}
+
+// Function to get client information by clientId
+async function getClient(token, clientId) {
+    console.log(`Getting client information for ${clientId}...`);
+    try {
+        const response = await axios.get(
+            `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients`,
+            {
+                headers: {
+                    'Authorization': `Bearer ${token}`
+                },
+                 params: {
+                    clientId: clientId
+                }
+            }
+        );
+        if (response.data.length === 0) {
+            console.log(`Client ${clientId} not found`);
+            return null;
+        }
+        console.log(`Client ${clientId} found.`);
+        return response.data[0];
+    } catch (error) {
+        handleAxiosError(error, `getting client ${clientId}`);
+         return null;
+    }
+}
+
+// Function to get client secret
+async function getClientSecret(token, clientId) {
+    console.log(`Getting client secret for ${clientId}...`);
+    const client = await getClient(token, clientId);
+    if (!client) {
+        console.log(`Client ${clientId} not found, no secret to get.`);
+        return null;
+    }
+    try {
+        const response = await axios.get(
+            `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
+            {
+                headers: { 'Authorization': `Bearer ${token}` }
+            }
+        );
+        console.log(`Client secret for ${clientId} retrieved.`);
+        return response.data.secret;
+    } catch (error) {
+        handleAxiosError(error, `getting client secret for ${clientId}`);
+        return null;
+    }
+}
+
+// Function to get redirect URIs
+async function getClientRedirectUris(token, clientId) {
+    console.log(`Getting redirect URIs for ${clientId}...`);
+    const client = await getClient(token, clientId);
+    if (!client) {
+        console.log(`Client ${clientId} not found, no redirect URIs to get.`);
+        return null;
+    }
+    try {
+        const response = await axios.get(
+            `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
+            {
+                headers: { 'Authorization': `Bearer ${token}` }
+            }
+        );
+        console.log(`Redirect URIs for ${clientId} retrieved.`);
+        return response.data.redirectUris;
+    } catch (error) {
+        handleAxiosError(error, `getting redirect URIs for ${clientId}`);
+        return null;
+    }
+}
+
+// Function to get default client scopes
+async function getDefaultClientScopes(token, clientId) {
+    console.log(`Getting default client scopes for ${clientId}...`);
+    const client = await getClient(token, clientId);
+     if (!client) {
+        console.log(`Client ${clientId} not found, no default client scopes to get.`);
+        return null;
+    }
+    try {
+        const response = await axios.get(
+            `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
+            {
+                headers: { 'Authorization': `Bearer ${token}` }
+            }
+        );
+         console.log(`Default client scopes for ${clientId} retrieved.`);
+        return response.data.defaultClientScopes;
+    } catch (error) {
+        handleAxiosError(error, `getting default client scopes for ${clientId}`);
+        return null;
+    }
+}
+
+// Function to get client mappers
+async function getClientMappers(token, clientId) {
+    console.log(`Getting client mappers for ${clientId}...`);
+     const client = await getClient(token, clientId);
+    if (!client) {
+        console.log(`Client ${clientId} not found, no mappers to get.`);
+        return [];
+    }
+    try {
+        const response = await axios.get(
+            `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/protocol-mappers/models`,
+            {
+                headers: {
+                    'Authorization': `Bearer ${token}`
+                }
+            }
+        );
+         console.log(`Client mappers for ${clientId} retrieved.`);
+        return response.data;
+    } catch (error) {
+        handleAxiosError(error, `getting client mappers for ${clientId}`);
+      return [];
+    }
+}
+
+// Function to update redirect URIs
+async function updateClientRedirectUris(token, clientId, redirectUris) {
+     console.log(`Updating redirect URIs for client ${clientId} to: ${JSON.stringify(redirectUris)}...`);
+    const client = await getClient(token, clientId);
+    if (!client) {
+        console.log(`Client ${clientId} not found, cannot update redirect URIs.`);
+        return;
+    }
+    try {
+        await axios.put(
+            `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
+            { ...client, redirectUris: redirectUris },
+            {
+                headers: {
+                    'Authorization': `Bearer ${token}`,
+                    'Content-Type': 'application/json'
+                }
+            }
+        );
+          console.log(`Redirect URIs for client ${clientId} updated successfully.`);
+    } catch (error) {
+       handleAxiosError(error, `updating redirect URIs for client ${clientId}`);
+    }
+}
+
+// Function to update default client scopes
+async function updateDefaultClientScopes(token, clientId, defaultClientScopes) {
+    console.log(`Updating default client scopes for client ${clientId} to: ${JSON.stringify(defaultClientScopes)}...`);
+     const client = await getClient(token, clientId);
+    if (!client) {
+        console.log(`Client ${clientId} not found, cannot update default client scopes.`);
+        return;
+    }
+    try {
+        await axios.put(
+            `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
+            { ...client, defaultClientScopes: defaultClientScopes },
+            {
+                headers: {
+                    'Authorization': `Bearer ${token}`,
+                    'Content-Type': 'application/json'
+                }
+            }
+        );
+        console.log(`Default client scopes for client ${clientId} updated successfully.`);
+    } catch (error) {
+        handleAxiosError(error, `updating default client scopes for client ${clientId}`);
+    }
+}
+
+// Main function to check client configuration
+async function checkClientConfig(token, clientId) {
+    console.log(`Checking configuration for client ${clientId}...`);
+    const clientSecret = await getClientSecret(token, clientId);
+    const redirectUris = await getClientRedirectUris(token, clientId);
+    const defaultClientScopes = await getDefaultClientScopes(token, clientId);
+    const mappers = await getClientMappers(token, clientId);
+
+    console.log(`Client Secret: ${clientSecret}`);
+    console.log(`Redirect URIs: ${JSON.stringify(redirectUris)}`);
+    console.log(`Default Client Scopes: ${JSON.stringify(defaultClientScopes)}`);
+    console.log("Mappers:");
+    mappers.forEach(mapper => console.log(`  - name: ${mapper.name}, type: ${mapper.protocolMapper}`));
+
+
+     // Check and update redirect URIs
+    const expectedRedirectUris = ["https://docs.mrx8086.com/*"];
+    if (JSON.stringify(redirectUris) !== JSON.stringify(expectedRedirectUris)) {
+         console.log(`Redirect URIs do not match expected values. Updating...`);
+        await updateClientRedirectUris(token, clientId, expectedRedirectUris);
+    }
+
+    // Check and update default client scopes
+    const expectedDefaultClientScopes = ["profile", "groups-paperless"];
+    if (JSON.stringify(defaultClientScopes) !== JSON.stringify(expectedDefaultClientScopes)) {
+         console.log(`Default client scopes do not match expected values. Updating...`);
+        await updateDefaultClientScopes(token, clientId, expectedDefaultClientScopes);
+    }
+
+    console.log(`Finished checking configuration for client ${clientId}.`);
+}
+
+
+// Main function
+async function main() {
+    try {
+        console.log('Starting Keycloak client configuration check...');
+        const token = await getAdminToken();
+        await checkClientConfig(token, PAPERLESS_CLIENT_ID);
+        console.log('Keycloak client configuration check completed successfully.');
+    } catch (error) {
+        console.error('Keycloak client configuration check failed:', error);
+        process.exit(1);
+    }
+}
+
+// Execute the script
+main();