|
@@ -0,0 +1,1634 @@
|
|
|
|
|
+diff --git a/.gitignore b/.gitignore
|
|
|
|
|
+index 7e57cb3..8994b5d 100644
|
|
|
|
|
+--- a/.gitignore
|
|
|
|
|
++++ b/.gitignore
|
|
|
|
|
+@@ -1,2 +1,3 @@
|
|
|
|
|
+ data/
|
|
|
|
|
+-config/nextcloud/
|
|
|
|
|
+\ No newline at end of file
|
|
|
|
|
++config/credentials
|
|
|
|
|
++config/nextcloud/
|
|
|
|
|
+diff --git a/ansible/site.yml b/ansible/site.yml
|
|
|
|
|
+index 22ea670..ded0d95 100644
|
|
|
|
|
+--- a/ansible/site.yml
|
|
|
|
|
++++ b/ansible/site.yml
|
|
|
|
|
+@@ -1,11 +1,22 @@
|
|
|
|
|
+-# automated-office/ansible/site.yml
|
|
|
|
|
+ ---
|
|
|
|
|
+ - name: Deploy Automated Office
|
|
|
|
|
+- hosts: all
|
|
|
|
|
++ hosts: localhost
|
|
|
|
|
+ become: true
|
|
|
|
|
+
|
|
|
|
|
++ pre_tasks:
|
|
|
|
|
++ - debug:
|
|
|
|
|
++ msg:
|
|
|
|
|
++ - "Client secret defined: {{ client_secret is defined }}"
|
|
|
|
|
++ - "Client secret length: {{ client_secret | default('') | length }}"
|
|
|
|
|
++ verbosity: 1
|
|
|
|
|
++
|
|
|
|
|
++ - name: "Verify required variables"
|
|
|
|
|
++ fail:
|
|
|
|
|
++ msg: "Client secret is not set or empty"
|
|
|
|
|
++ when: client_secret is not defined or client_secret | default('') | trim == ''
|
|
|
|
|
++
|
|
|
|
|
+ roles:
|
|
|
|
|
+- - common
|
|
|
|
|
+- - docker
|
|
|
|
|
+- - nginx
|
|
|
|
|
+- - services
|
|
|
|
|
+\ No newline at end of file
|
|
|
|
|
++ - role: common
|
|
|
|
|
++ - role: docker
|
|
|
|
|
++ - role: nginx
|
|
|
|
|
++ - role: services
|
|
|
|
|
+\ No newline at end of file
|
|
|
|
|
+diff --git a/config/credentials/credentials_2024-12-11_18-28-21.txt.gpg b/config/credentials/credentials_2024-12-11_18-28-21.txt.gpg
|
|
|
|
|
+deleted file mode 100644
|
|
|
|
|
+index b43471e..0000000
|
|
|
|
|
+Binary files a/config/credentials/credentials_2024-12-11_18-28-21.txt.gpg and /dev/null differ
|
|
|
|
|
+diff --git a/config/nginx/sites-available/keycloak b/config/nginx/sites-available/keycloak
|
|
|
|
|
+index 2c2181a..cf437a2 100644
|
|
|
|
|
+--- a/config/nginx/sites-available/keycloak
|
|
|
|
|
++++ b/config/nginx/sites-available/keycloak
|
|
|
|
|
+@@ -5,7 +5,7 @@ upstream keycloak_upstream {
|
|
|
|
|
+ server {
|
|
|
|
|
+ listen 80;
|
|
|
|
|
+ server_name auth.mrx8086.com;
|
|
|
|
|
+-
|
|
|
|
|
++
|
|
|
|
|
+ # Redirect HTTP to HTTPS
|
|
|
|
|
+ return 301 https://$host$request_uri;
|
|
|
|
|
+ }
|
|
|
|
|
+@@ -30,35 +30,49 @@ server {
|
|
|
|
|
+ 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;
|
|
|
|
|
+-
|
|
|
|
|
++
|
|
|
|
|
+ # Content Security Policy
|
|
|
|
|
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; frame-src 'self'; frame-ancestors 'self'; connect-src 'self'" always;
|
|
|
|
|
+
|
|
|
|
|
+- # Proxy settings for all locations
|
|
|
|
|
++ # Proxy settings - Added X-Forwarded headers here to apply to all proxied locations
|
|
|
|
|
+ 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-Ssl on; # Optional, but explicit
|
|
|
|
|
++ proxy_set_header X-Forwarded-Port $server_port;
|
|
|
|
|
+ proxy_set_header X-Forwarded-Host $host;
|
|
|
|
|
+- proxy_set_header X-Forwarded-Port 443;
|
|
|
|
|
+ proxy_set_header Host $host;
|
|
|
|
|
+ proxy_http_version 1.1;
|
|
|
|
|
+-
|
|
|
|
|
++
|
|
|
|
|
+ # Root location for the main application
|
|
|
|
|
+ location / {
|
|
|
|
|
+ proxy_pass http://keycloak_upstream;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
++ # Specific location for the token endpoint
|
|
|
|
|
++ location ~ ^/auth/realms/[^/]+/protocol/openid-connect/token$ {
|
|
|
|
|
++ proxy_pass http://keycloak_upstream;
|
|
|
|
|
++ proxy_buffer_size 128k;
|
|
|
|
|
++ proxy_buffers 4 256k;
|
|
|
|
|
++ proxy_busy_buffers_size 256k;
|
|
|
|
|
++ # WebSocket support (likely not needed for token endpoint, but keeping for consistency)
|
|
|
|
|
++ proxy_set_header Upgrade $http_upgrade;
|
|
|
|
|
++ proxy_set_header Connection "upgrade";
|
|
|
|
|
++ # Timeouts
|
|
|
|
|
++ proxy_connect_timeout 60s;
|
|
|
|
|
++ proxy_send_timeout 60s;
|
|
|
|
|
++ proxy_read_timeout 60s;
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
+ # Keycloak required paths
|
|
|
|
|
+ location /realms/ {
|
|
|
|
|
+ proxy_pass http://keycloak_upstream;
|
|
|
|
|
+ proxy_buffer_size 128k;
|
|
|
|
|
+ proxy_buffers 4 256k;
|
|
|
|
|
+ proxy_busy_buffers_size 256k;
|
|
|
|
|
+-
|
|
|
|
|
+ # WebSocket support
|
|
|
|
|
+ proxy_set_header Upgrade $http_upgrade;
|
|
|
|
|
+ proxy_set_header Connection "upgrade";
|
|
|
|
|
+-
|
|
|
|
|
+ # Timeouts
|
|
|
|
|
+ proxy_connect_timeout 60s;
|
|
|
|
|
+ proxy_send_timeout 60s;
|
|
|
|
|
+@@ -67,7 +81,7 @@ server {
|
|
|
|
|
+
|
|
|
|
|
+ location /resources/ {
|
|
|
|
|
+ proxy_pass http://keycloak_upstream;
|
|
|
|
|
+-
|
|
|
|
|
++
|
|
|
|
|
+ # Cache settings for static resources
|
|
|
|
|
+ proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
|
|
|
|
|
+ proxy_cache_valid 200 1d;
|
|
|
|
|
+diff --git a/config/nginx/sites-available/nextcloud b/config/nginx/sites-available/nextcloud
|
|
|
|
|
+index c384e10..8357df3 100644
|
|
|
|
|
+--- a/config/nginx/sites-available/nextcloud
|
|
|
|
|
++++ b/config/nginx/sites-available/nextcloud
|
|
|
|
|
+@@ -1,12 +1,10 @@
|
|
|
|
|
+ upstream nextcloud_upstream {
|
|
|
|
|
+- server 172.19.0.3:80; # Die IP wird später durch die tatsächliche Container-IP ersetzt
|
|
|
|
|
++ server 172.19.0.3:80; # SICHERSTELLEN, DASS DIES DIE KORREKTE IP IST
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ server {
|
|
|
|
|
+ listen 80;
|
|
|
|
|
+ server_name cloud.mrx8086.com;
|
|
|
|
|
+-
|
|
|
|
|
+- # Redirect HTTP to HTTPS
|
|
|
|
|
+ return 301 https://$host$request_uri;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+@@ -14,27 +12,19 @@ server {
|
|
|
|
|
+ listen 443 ssl;
|
|
|
|
|
+ server_name cloud.mrx8086.com;
|
|
|
|
|
+
|
|
|
|
|
+- # SSL Configuration
|
|
|
|
|
++ # SSL Configuration (wie zuvor)
|
|
|
|
|
+ ssl_certificate /etc/nginx/ssl/mrx8086.com/fullchain.pem;
|
|
|
|
|
+ ssl_certificate_key /etc/nginx/ssl/mrx8086.com/privkey.pem;
|
|
|
|
|
+- ssl_session_timeout 1d;
|
|
|
|
|
+- ssl_session_tickets off;
|
|
|
|
|
++ # ... weitere SSL-Einstellungen ...
|
|
|
|
|
+
|
|
|
|
|
+- # Modern SSL configuration
|
|
|
|
|
+- ssl_protocols TLSv1.2 TLSv1.3;
|
|
|
|
|
+- ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
|
|
|
|
+- ssl_prefer_server_ciphers off;
|
|
|
|
|
+-
|
|
|
|
|
+- # Security headers
|
|
|
|
|
++ # Security headers (wie zuvor)
|
|
|
|
|
+ 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;
|
|
|
|
|
+-
|
|
|
|
|
+- # Content Security Policy für Nextcloud
|
|
|
|
|
+ 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 settings (wie zuvor)
|
|
|
|
|
+ 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;
|
|
|
|
|
+@@ -43,31 +33,36 @@ server {
|
|
|
|
|
+ proxy_set_header Host $host;
|
|
|
|
|
+ proxy_http_version 1.1;
|
|
|
|
|
+
|
|
|
|
|
+- # Nextcloud specific settings
|
|
|
|
|
++ # Nextcloud specific settings (wie zuvor)
|
|
|
|
|
+ client_max_body_size 512M;
|
|
|
|
|
+ fastcgi_buffers 64 4K;
|
|
|
|
|
+-
|
|
|
|
|
++
|
|
|
|
|
++ # Expliziter location-Block für den OpenID Connect Callback
|
|
|
|
|
++ location /apps/sociallogin/custom_oidc/keycloak {
|
|
|
|
|
++ proxy_pass http://nextcloud_upstream;
|
|
|
|
|
++ 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;
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
+ # Root location
|
|
|
|
|
+ location / {
|
|
|
|
|
+ proxy_pass http://nextcloud_upstream;
|
|
|
|
|
+-
|
|
|
|
|
+- # WebSocket support
|
|
|
|
|
+ proxy_set_header Upgrade $http_upgrade;
|
|
|
|
|
+ proxy_set_header Connection "upgrade";
|
|
|
|
|
+-
|
|
|
|
|
+- # Timeouts
|
|
|
|
|
+ proxy_connect_timeout 60s;
|
|
|
|
|
+ proxy_send_timeout 60s;
|
|
|
|
|
+ proxy_read_timeout 60s;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+- # Block sensitive paths
|
|
|
|
|
++ # Block sensitive paths (wie zuvor)
|
|
|
|
|
+ location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) {
|
|
|
|
|
+ deny all;
|
|
|
|
|
+ return 404;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+- # Deny access to hidden files
|
|
|
|
|
++ # Deny access to hidden files (wie zuvor)
|
|
|
|
|
+ location ~ /\. {
|
|
|
|
|
+ deny all;
|
|
|
|
|
+ return 404;
|
|
|
|
|
+diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
|
|
|
|
|
+index 0163334..ccbcd3b 100644
|
|
|
|
|
+--- a/docker/docker-compose.yml
|
|
|
|
|
++++ b/docker/docker-compose.yml
|
|
|
|
|
+@@ -34,8 +34,10 @@ services:
|
|
|
|
|
+ - keycloak-network
|
|
|
|
|
+ depends_on:
|
|
|
|
|
+ - keycloak-db
|
|
|
|
|
++ extra_hosts:
|
|
|
|
|
++ - "cloud.mrx8086.com:172.23.171.133"
|
|
|
|
|
+ healthcheck:
|
|
|
|
|
+- test: ["CMD", "curl", "-f", "http://localhost:8080/health/ready"]
|
|
|
|
|
++ test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
|
|
|
|
+ interval: 30s
|
|
|
|
|
+ timeout: 10s
|
|
|
|
|
+ retries: 3
|
|
|
|
|
+@@ -48,7 +50,7 @@ services:
|
|
|
|
|
+ POSTGRES_USER: ${KC_DB_USERNAME}
|
|
|
|
|
+ POSTGRES_PASSWORD: ${KC_DB_PASSWORD}
|
|
|
|
|
+ volumes:
|
|
|
|
|
+- - ../data/keycloak/db:/var/lib/postgresql/data
|
|
|
|
|
++ - ../data/keycloak-db:/var/lib/postgresql/data
|
|
|
|
|
+ networks:
|
|
|
|
|
+ - keycloak-network
|
|
|
|
|
+ restart: unless-stopped
|
|
|
|
|
+@@ -63,6 +65,13 @@ services:
|
|
|
|
|
+ image: nextcloud:latest
|
|
|
|
|
+ container_name: nextcloud
|
|
|
|
|
+ restart: unless-stopped
|
|
|
|
|
++ ports:
|
|
|
|
|
++ - "8081:80"
|
|
|
|
|
++ volumes:
|
|
|
|
|
++ - ../data/nextcloud:/var/www/html
|
|
|
|
|
++ - ../config/nextcloud/config:/var/www/html/config
|
|
|
|
|
++ - ../config/nextcloud/custom_apps:/var/www/html/custom_apps
|
|
|
|
|
++ - ../data/nextcloud-db:/var/lib/mysql
|
|
|
|
|
+ environment:
|
|
|
|
|
+ - MYSQL_HOST=nextcloud-db
|
|
|
|
|
+ - MYSQL_DATABASE=nextcloud
|
|
|
|
|
+@@ -74,16 +83,24 @@ services:
|
|
|
|
|
+ - OVERWRITEPROTOCOL=https
|
|
|
|
|
+ - OVERWRITEHOST=cloud.mrx8086.com
|
|
|
|
|
+ - OVERWRITEWEBROOT=/
|
|
|
|
|
+- - TRUSTED_PROXIES=172.18.0.0/16
|
|
|
|
|
+- volumes:
|
|
|
|
|
+- - ../data/nextcloud:/var/www/html
|
|
|
|
|
+- - ../config/nextcloud/config:/var/www/html/config
|
|
|
|
|
+- - ../config/nextcloud/custom_apps:/var/www/html/custom_apps
|
|
|
|
|
+- - ../config/nextcloud/data:/var/www/html/data
|
|
|
|
|
++ - TRUSTED_PROXIES=172.19.0.0/16
|
|
|
|
|
++ - NEXTCLOUD_URL=https://cloud.mrx8086.com
|
|
|
|
|
++ - NEXTCLOUD_DEBUG=1
|
|
|
|
|
++ - NEXTCLOUD_CONFIG_CUSTOM_SCOPE="openid profile groups-nextcloud"
|
|
|
|
|
++ healthcheck:
|
|
|
|
|
++ test: ["CMD", "curl", "-f", "http://localhost:80/"]
|
|
|
|
|
++ interval: 30s
|
|
|
|
|
++ timeout: 10s
|
|
|
|
|
++ retries: 3
|
|
|
|
|
+ networks:
|
|
|
|
|
+ - nextcloud-network
|
|
|
|
|
+ depends_on:
|
|
|
|
|
+ - nextcloud-db
|
|
|
|
|
++ extra_hosts:
|
|
|
|
|
++ - "auth.mrx8086.com:172.23.171.133"
|
|
|
|
|
++ dns:
|
|
|
|
|
++ - 8.8.8.8
|
|
|
|
|
++ - 8.8.4.4
|
|
|
|
|
+
|
|
|
|
|
+ nextcloud-db:
|
|
|
|
|
+ image: mariadb:10.6
|
|
|
|
|
+diff --git a/docs/context/scripts/setup_environment.sh b/docs/context/scripts/setup_environment.sh
|
|
|
|
|
+index 9a1b2da..1244eb8 100644
|
|
|
|
|
+--- a/docs/context/scripts/setup_environment.sh
|
|
|
|
|
++++ b/docs/context/scripts/setup_environment.sh
|
|
|
|
|
+@@ -1,4 +1,5 @@
|
|
|
|
|
+ #!/bin/bash
|
|
|
|
|
++set -e
|
|
|
|
|
+
|
|
|
|
|
+ # Ensure we're in the project root directory
|
|
|
|
|
+ PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
|
|
|
+@@ -8,7 +9,8 @@ cd "${PROJECT_ROOT}"
|
|
|
|
|
+ CREDENTIALS_DIR="config/credentials"
|
|
|
|
|
+ DOCKER_DIR="docker"
|
|
|
|
|
+ KEYCLOAK_SCRIPTS_DIR="scripts/setup/keycloak"
|
|
|
|
|
+-
|
|
|
|
|
++ANSIBLE_PLAYBOOK="ansible/site.yml"
|
|
|
|
|
++ANSIBLE_INVENTORY="ansible/inventory/staging/hosts"
|
|
|
|
|
+
|
|
|
|
|
+ # Create necessary directories
|
|
|
|
|
+ mkdir -p "${CREDENTIALS_DIR}"
|
|
|
|
|
+@@ -33,7 +35,7 @@ NEXTCLOUD_DB_USER=$(generate_password)
|
|
|
|
|
+ NEXTCLOUD_DB_PASSWORD=$(generate_password)
|
|
|
|
|
+ NEXTCLOUD_ADMIN_USER=$(generate_password)
|
|
|
|
|
+ NEXTCLOUD_ADMIN_PASSWORD=$(generate_password)
|
|
|
|
|
+-
|
|
|
|
|
++KEYCLOAK_NEXTCLOUD_CLIENT_SECRET=$(generate_password)
|
|
|
|
|
+
|
|
|
|
|
+ # Create .env file in docker directory
|
|
|
|
|
+ cat > "${DOCKER_DIR}/.env" << EOL
|
|
|
|
|
+@@ -64,6 +66,7 @@ PAPERLESS_CLIENT_ID=paperless
|
|
|
|
|
+ NODERED_CLIENT_ID=nodered
|
|
|
|
|
+ TESTADMIN_PASSWORD=${TESTADMIN_PASSWORD}
|
|
|
|
|
+ TESTUSER_PASSWORD=${TESTUSER_PASSWORD}
|
|
|
|
|
++KEYCLOAK_NEXTCLOUD_CLIENT_SECRET=${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}
|
|
|
|
|
+ EOL
|
|
|
|
|
+
|
|
|
|
|
+ # Create encrypted credentials documentation
|
|
|
|
|
+@@ -77,8 +80,10 @@ Password: ${KEYCLOAK_ADMIN_PASSWORD}
|
|
|
|
|
+ Keycloak Database Credentials:
|
|
|
|
|
+ Username: keycloak
|
|
|
|
|
+ Password: ${KC_DB_PASSWORD}
|
|
|
|
|
++
|
|
|
|
|
+ Test Admin Credentials:
|
|
|
|
|
+ Password: ${TESTADMIN_PASSWORD}
|
|
|
|
|
++
|
|
|
|
|
+ Test User Credentials:
|
|
|
|
|
+ Password: ${TESTUSER_PASSWORD}
|
|
|
|
|
+
|
|
|
|
|
+@@ -86,6 +91,7 @@ Nextcloud Database Credentials:
|
|
|
|
|
+ Root Password: ${NEXTCLOUD_DB_ROOT_PASSWORD}
|
|
|
|
|
+ User: ${NEXTCLOUD_DB_USER}
|
|
|
|
|
+ Password: ${NEXTCLOUD_DB_PASSWORD}
|
|
|
|
|
++
|
|
|
|
|
+ Nextcloud Admin Credentials:
|
|
|
|
|
+ Username: ${NEXTCLOUD_ADMIN_USER}
|
|
|
|
|
+ Password: ${NEXTCLOUD_ADMIN_PASSWORD}
|
|
|
|
|
+@@ -101,4 +107,40 @@ echo ".env file for docker-compose has been created in: ${DOCKER_DIR}/.env"
|
|
|
|
|
+ echo ".env file for setup_realm.js has been created in: ${KEYCLOAK_SCRIPTS_DIR}/.env"
|
|
|
|
|
+ echo ""
|
|
|
|
|
+ echo "To view credentials, use:"
|
|
|
|
|
+-echo "gpg -d ${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt.gpg"
|
|
|
|
|
+\ No newline at end of file
|
|
|
|
|
++echo "gpg -d ${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt.gpg"
|
|
|
|
|
++
|
|
|
|
|
++echo ">>> Nextcloud Konfiguration..."
|
|
|
|
|
++
|
|
|
|
|
++# Verify if variable is set from earlier in the script
|
|
|
|
|
++echo ">>> Debug: Checking original variable..."
|
|
|
|
|
++echo ">>> Debug: KEYCLOAK_NEXTCLOUD_CLIENT_SECRET = ${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}"
|
|
|
|
|
++
|
|
|
|
|
++# Try reading from .env file if variable is empty
|
|
|
|
|
++if [ -z "${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}" ]; then
|
|
|
|
|
++ echo ">>> Debug: Variable is empty, trying to read from .env file..."
|
|
|
|
|
++ KEYCLOAK_NEXTCLOUD_CLIENT_SECRET=$(grep KEYCLOAK_NEXTCLOUD_CLIENT_SECRET "${KEYCLOAK_SCRIPTS_DIR}/.env" | cut -d '=' -f2)
|
|
|
|
|
++ echo ">>> Debug: Value from .env file = ${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}"
|
|
|
|
|
++fi
|
|
|
|
|
++
|
|
|
|
|
++# Ensure we have a value
|
|
|
|
|
++if [ -z "${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}" ]; then
|
|
|
|
|
++ echo ">>> Error: Could not get client secret value"
|
|
|
|
|
++ exit 1
|
|
|
|
|
++fi
|
|
|
|
|
++
|
|
|
|
|
++# Escape special characters in the secret for JSON
|
|
|
|
|
++ESCAPED_SECRET=$(echo "$KEYCLOAK_NEXTCLOUD_CLIENT_SECRET" | sed 's/["\]/\\&/g')
|
|
|
|
|
++echo ">>> Debug: Escaped secret = $ESCAPED_SECRET"
|
|
|
|
|
++
|
|
|
|
|
++# Create the extra vars
|
|
|
|
|
++EXTRA_VARS="{\"client_secret\": \"$ESCAPED_SECRET\"}"
|
|
|
|
|
++echo ">>> Debug: Extra vars = $EXTRA_VARS"
|
|
|
|
|
++
|
|
|
|
|
++# Run Ansible with the extra vars
|
|
|
|
|
++sudo ansible-playbook \
|
|
|
|
|
++ -i "$ANSIBLE_INVENTORY" \
|
|
|
|
|
++ "$ANSIBLE_PLAYBOOK" \
|
|
|
|
|
++ --extra-vars "$EXTRA_VARS" \
|
|
|
|
|
++ -v
|
|
|
|
|
++
|
|
|
|
|
++echo ">>> Fertig"
|
|
|
|
|
+\ No newline at end of file
|
|
|
|
|
+diff --git a/scripts/install/setup_environment.sh b/scripts/install/setup_environment.sh
|
|
|
|
|
+old mode 100644
|
|
|
|
|
+new mode 100755
|
|
|
|
|
+index aba63dc..5b7bc0e
|
|
|
|
|
+--- a/scripts/install/setup_environment.sh
|
|
|
|
|
++++ b/scripts/install/setup_environment.sh
|
|
|
|
|
+@@ -1,31 +1,163 @@
|
|
|
|
|
+ #!/bin/bash
|
|
|
|
|
++set -e
|
|
|
|
|
+
|
|
|
|
|
+ # Ensure we're in the project root directory
|
|
|
|
|
+ PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
|
|
|
+-cd "${PROJECT_ROOT}"
|
|
|
|
|
+
|
|
|
|
|
+ # Define directories relative to project root
|
|
|
|
|
+-CREDENTIALS_DIR="config/credentials"
|
|
|
|
|
+-DOCKER_DIR="docker"
|
|
|
|
|
++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
|
|
|
|
|
+-mkdir -p "${CREDENTIALS_DIR}"
|
|
|
|
|
+-mkdir -p "${DOCKER_DIR}"
|
|
|
|
|
++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 24 | tr -dc 'a-zA-Z0-9' | head -c 24
|
|
|
|
|
++ 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')
|
|
|
|
|
+
|
|
|
|
|
+-# Generate passwords
|
|
|
|
|
+-KEYCLOAK_ADMIN_PASSWORD=$(generate_password)
|
|
|
|
|
+-KC_DB_PASSWORD=$(generate_password)
|
|
|
|
|
++# 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 ">>> GPG encryption failed. Attempting GPG encryption with password workaround."
|
|
|
|
|
++ # Attempt encryption with passphrase workaround
|
|
|
|
|
++ if echo "$CREDENTIALS_CONTENT" | gpg --batch --passphrase "$GPG_PASSPHRASE" --symmetric --cipher-algo AES256 -vvv -o "${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt.gpg"; then
|
|
|
|
|
++ echo ">>> Credentials encrypted successfully using gpg with passphrase workaround."
|
|
|
|
|
++ else
|
|
|
|
|
++ echo ">>> GPG encryption with passphrase workaround failed"
|
|
|
|
|
++ exit 1
|
|
|
|
|
++ fi
|
|
|
|
|
++ fi
|
|
|
|
|
++ fi
|
|
|
|
|
++rm "$TEMP_FILE"
|
|
|
|
|
+
|
|
|
|
|
+ # Create .env file in docker directory
|
|
|
|
|
+-cat > "${DOCKER_DIR}/.env" << EOL
|
|
|
|
|
++DOCKER_ENV_CONTENT=$(cat <<EOL
|
|
|
|
|
+ # Generated on ${SETUP_DATE}
|
|
|
|
|
+ # Keycloak Admin
|
|
|
|
|
+ KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_ADMIN_PASSWORD}
|
|
|
|
|
+@@ -34,27 +166,69 @@ KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_ADMIN_PASSWORD}
|
|
|
|
|
+ KC_DB_USERNAME=keycloak
|
|
|
|
|
+ KC_DB_PASSWORD=${KC_DB_PASSWORD}
|
|
|
|
|
+ EOL
|
|
|
|
|
++)
|
|
|
|
|
++create_env_file "$DOCKER_DIR/.env" "$DOCKER_ENV_CONTENT"
|
|
|
|
|
+
|
|
|
|
|
+-# Create encrypted credentials documentation
|
|
|
|
|
+-cat > "${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt" << EOL
|
|
|
|
|
+-Setup Date: ${SETUP_DATE}
|
|
|
|
|
++# Create .env file in scripts/setup/keycloak directory
|
|
|
|
|
++KEYCLOAK_ENV_CONTENT=$(cat <<EOL
|
|
|
|
|
++KEYCLOAK_URL=https://auth.mrx8086.com
|
|
|
|
|
++KEYCLOAK_ADMIN_USER=admin
|
|
|
|
|
++KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_ADMIN_PASSWORD}
|
|
|
|
|
++NEXTCLOUD_CLIENT_ID=nextcloud
|
|
|
|
|
++PAPERLESS_CLIENT_ID=paperless
|
|
|
|
|
++NODERED_CLIENT_ID=nodered
|
|
|
|
|
++TESTADMIN_PASSWORD=${TESTADMIN_PASSWORD}
|
|
|
|
|
++TESTUSER_PASSWORD=${TESTUSER_PASSWORD}
|
|
|
|
|
++TESTSERVICEUSER_PASSWORD=${TESTSERVICEUSER_PASSWORD}
|
|
|
|
|
++KEYCLOAK_NEXTCLOUD_CLIENT_SECRET=${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}
|
|
|
|
|
++EOL
|
|
|
|
|
++)
|
|
|
|
|
++create_env_file "$KEYCLOAK_SETUP_DIR/.env" "$KEYCLOAK_ENV_CONTENT"
|
|
|
|
|
+
|
|
|
|
|
+-Keycloak Admin Credentials:
|
|
|
|
|
+-Username: admin
|
|
|
|
|
+-Password: ${KEYCLOAK_ADMIN_PASSWORD}
|
|
|
|
|
++echo ">>> Environment setup completed!"
|
|
|
|
|
+
|
|
|
|
|
+-Keycloak Database Credentials:
|
|
|
|
|
+-Username: keycloak
|
|
|
|
|
+-Password: ${KC_DB_PASSWORD}
|
|
|
|
|
+-EOL
|
|
|
|
|
++# --------------- KEYCLOAK KONFIGURATION ---------------
|
|
|
|
|
++echo ">>> Keycloak Konfiguration..."
|
|
|
|
|
++cd "$KEYCLOAK_SETUP_DIR"
|
|
|
|
|
++
|
|
|
|
|
++echo ">>> Starte setup_realm.js"
|
|
|
|
|
++node setup_realm.js
|
|
|
|
|
++
|
|
|
|
|
++cd "$PROJECT_ROOT"
|
|
|
|
|
++
|
|
|
|
|
++# --------------- NEXTCLOUD KONFIGURATION ---------------
|
|
|
|
|
++echo ">>> Nextcloud Konfiguration..."
|
|
|
|
|
++
|
|
|
|
|
++# Verify if variable is set from earlier in the script
|
|
|
|
|
++echo ">>> Debug: Checking original variable..."
|
|
|
|
|
++echo ">>> Debug: KEYCLOAK_NEXTCLOUD_CLIENT_SECRET = ${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}"
|
|
|
|
|
++
|
|
|
|
|
++# Try reading from .env file if variable is empty
|
|
|
|
|
++if [ -z "${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}" ]; then
|
|
|
|
|
++ echo ">>> Debug: Variable is empty, trying to read from .env file..."
|
|
|
|
|
++ KEYCLOAK_NEXTCLOUD_CLIENT_SECRET=$(grep KEYCLOAK_NEXTCLOUD_CLIENT_SECRET "${KEYCLOAK_SETUP_DIR}/.env" | cut -d '=' -f2)
|
|
|
|
|
++ echo ">>> Debug: Value from .env file = ${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}"
|
|
|
|
|
++fi
|
|
|
|
|
++
|
|
|
|
|
++# Ensure we have a value
|
|
|
|
|
++if [ -z "${KEYCLOAK_NEXTCLOUD_CLIENT_SECRET}" ]; then
|
|
|
|
|
++ echo ">>> Error: Could not get client secret value"
|
|
|
|
|
++ exit 1
|
|
|
|
|
++fi
|
|
|
|
|
++
|
|
|
|
|
++# Escape special characters in the secret for JSON
|
|
|
|
|
++ESCAPED_SECRET=$(echo "$KEYCLOAK_NEXTCLOUD_CLIENT_SECRET" | sed 's/["\]/\\&/g')
|
|
|
|
|
++echo ">>> Debug: Escaped secret = $ESCAPED_SECRET"
|
|
|
|
|
++
|
|
|
|
|
++# Create the extra vars
|
|
|
|
|
++EXTRA_VARS="{\"client_secret\": \"$ESCAPED_SECRET\"}"
|
|
|
|
|
++echo ">>> Debug: Extra vars = $EXTRA_VARS"
|
|
|
|
|
+
|
|
|
|
|
+-# Encrypt credentials file
|
|
|
|
|
+-gpg --symmetric --cipher-algo AES256 "${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt"
|
|
|
|
|
+-rm "${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt"
|
|
|
|
|
++# Run Ansible with the extra vars
|
|
|
|
|
++sudo ansible-playbook \
|
|
|
|
|
++ -i "$ANSIBLE_INVENTORY" \
|
|
|
|
|
++ "$ANSIBLE_PLAYBOOK" \
|
|
|
|
|
++ --extra-vars "$EXTRA_VARS" \
|
|
|
|
|
++ -v
|
|
|
|
|
+
|
|
|
|
|
+-echo "Environment setup completed!"
|
|
|
|
|
+-echo "Credentials have been saved and encrypted in: ${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt.gpg"
|
|
|
|
|
+-echo ".env file has been created in: ${DOCKER_DIR}/.env"
|
|
|
|
|
+-echo ""
|
|
|
|
|
+-echo "To view credentials, use:"
|
|
|
|
|
+-echo "gpg -d ${CREDENTIALS_DIR}/credentials_${SETUP_DATE}.txt.gpg"
|
|
|
|
|
+\ No newline at end of file
|
|
|
|
|
++echo ">>> Fertig"
|
|
|
|
|
+\ No newline at end of file
|
|
|
|
|
+diff --git a/scripts/setup/keycloak/.env b/scripts/setup/keycloak/.env
|
|
|
|
|
+index 8c6b3c3..0087b01 100644
|
|
|
|
|
+--- a/scripts/setup/keycloak/.env
|
|
|
|
|
++++ b/scripts/setup/keycloak/.env
|
|
|
|
|
+@@ -6,4 +6,6 @@ PAPERLESS_CLIENT_ID=paperless
|
|
|
|
|
+ NODERED_CLIENT_ID=nodered
|
|
|
|
|
+ TESTADMIN_PASSWORD=TestAdminPwd
|
|
|
|
|
+ TESTUSER_PASSWORD=TestUserPwd
|
|
|
|
|
+-KEYCLOAK_NEXTCLOUD_CLIENT_SECRET=D939xgzoxi58T2XZShdUPZP4gsI0kBOu
|
|
|
|
|
+\ No newline at end of file
|
|
|
|
|
++TESTSERVICEUSER_PASSWORD=TestServiceUserPwd
|
|
|
|
|
++KEYCLOAK_NEXTCLOUD_CLIENT_SECRET=OSbJ08zyjBWChwBR7S6c1q4sU0d8zvEK
|
|
|
|
|
++NEXTCLOUD_URL=https://cloud.mrx8086.com
|
|
|
|
|
+\ No newline at end of file
|
|
|
|
|
+diff --git a/scripts/setup/keycloak/setup_realm.js b/scripts/setup/keycloak/setup_realm.js
|
|
|
|
|
+index 9f67b1e..784d543 100644
|
|
|
|
|
+--- a/scripts/setup/keycloak/setup_realm.js
|
|
|
|
|
++++ b/scripts/setup/keycloak/setup_realm.js
|
|
|
|
|
+@@ -1,19 +1,26 @@
|
|
|
|
|
+ import dotenv from 'dotenv';
|
|
|
|
|
+ import axios from 'axios';
|
|
|
|
|
+
|
|
|
|
|
+-// Lade Umgebungsvariablen
|
|
|
|
|
++// Load environment variables
|
|
|
|
|
+ dotenv.config();
|
|
|
|
|
++console.log('Environment variables loaded.');
|
|
|
|
|
+
|
|
|
|
|
+-// Konfigurationskonstanten
|
|
|
|
|
++// 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 IDs und deren Konfiguration
|
|
|
|
|
++console.log('Configuration constants set:', { KEYCLOAK_URL, ADMIN_USERNAME, REALM_NAME });
|
|
|
|
|
++
|
|
|
|
|
++// Client IDs and their configuration
|
|
|
|
|
+ const CLIENTS = {
|
|
|
|
|
+ [process.env.NEXTCLOUD_CLIENT_ID || 'nextcloud']: {
|
|
|
|
|
+- redirectUris: ["https://cloud.mrx8086.com/*"]
|
|
|
|
|
++ redirectUris: [
|
|
|
|
|
++ `https://cloud.mrx8086.com/apps/sociallogin/custom_oidc/keycloak`,
|
|
|
|
|
++ `https://cloud.mrx8086.com/apps/user_oidc/code`
|
|
|
|
|
++ ],
|
|
|
|
|
++ postLogoutRedirectUris: ["https://cloud.mrx8086.com/*"]
|
|
|
|
|
+ },
|
|
|
|
|
+ [process.env.PAPERLESS_CLIENT_ID || 'paperless']: {
|
|
|
|
|
+ redirectUris: ["https://docs.mrx8086.com/*"]
|
|
|
|
|
+@@ -23,16 +30,18 @@ const CLIENTS = {
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+-// Hilfsfunktion für API-Fehlerbehandlung
|
|
|
|
|
++console.log('CLIENTS configuration:', CLIENTS);
|
|
|
|
|
++
|
|
|
|
|
++// 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,
|
|
|
|
|
+- });
|
|
|
|
|
++ console.error('Request:', {
|
|
|
|
|
++ method: config.method,
|
|
|
|
|
++ url: config.url,
|
|
|
|
|
++ headers: config.headers,
|
|
|
|
|
++ data: config.data,
|
|
|
|
|
++ });
|
|
|
|
|
+ }
|
|
|
|
|
+ if (error.response) {
|
|
|
|
|
+ console.error('Response:', {
|
|
|
|
|
+@@ -45,8 +54,9 @@ const handleAxiosError = (error, operation, config, response) => {
|
|
|
|
|
+ throw error;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+-// Admin Token abrufen
|
|
|
|
|
++// 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`,
|
|
|
|
|
+@@ -62,165 +72,146 @@ async function getAdminToken() {
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
++ console.log('Admin token received.');
|
|
|
|
|
+ return response.data.access_token;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+- handleAxiosError(error, 'getting admin token');
|
|
|
|
|
++ handleAxiosError(error, 'getting admin token');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-// Prüfen ob Realm existiert
|
|
|
|
|
++// Check if Realm exists
|
|
|
|
|
+ async function checkRealmExists(token) {
|
|
|
|
|
++ console.log(`Checking if realm ${REALM_NAME} exists...`);
|
|
|
|
|
+ try {
|
|
|
|
|
+- await axios.get(
|
|
|
|
|
+- `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}`,
|
|
|
|
|
+- {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Authorization': `Bearer ${token}`
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
+- );
|
|
|
|
|
++ await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}`, {
|
|
|
|
|
++ headers: { 'Authorization': `Bearer ${token}` }
|
|
|
|
|
++ });
|
|
|
|
|
++ console.log(`Realm ${REALM_NAME} exists.`);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ if (error.response?.status === 404) {
|
|
|
|
|
++ console.log(`Realm ${REALM_NAME} does not exist.`);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ handleAxiosError(error, 'checking realm existence');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-
|
|
|
|
|
+-// Funktion um Client Infos abzufragen
|
|
|
|
|
+-async function getClient(token, clientId) {
|
|
|
|
|
++// Function to get client information by clientId
|
|
|
|
|
++async function getClientByClientId(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
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
+- );
|
|
|
|
|
++ const response = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients`, {
|
|
|
|
|
++ headers: { 'Authorization': `Bearer ${token}` },
|
|
|
|
|
++ params: { clientId }
|
|
|
|
|
++ });
|
|
|
|
|
+ if (response.data.length === 0) {
|
|
|
|
|
+- console.error(`Client ${clientId} not found`);
|
|
|
|
|
++ console.log(`Client ${clientId} not found`);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+-
|
|
|
|
|
++ console.log(`Client ${clientId} found.`);
|
|
|
|
|
+ return response.data[0];
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+- handleAxiosError(error, `getting client ${clientId}`);
|
|
|
|
|
++ handleAxiosError(error, `getting client ${clientId}`);
|
|
|
|
|
++ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-// Prüfen ob Client existiert
|
|
|
|
|
+-async function checkClientExists(token, clientId) {
|
|
|
|
|
+- const client = await getClient(token, clientId);
|
|
|
|
|
+- return !!client;
|
|
|
|
|
+-}
|
|
|
|
|
+-
|
|
|
|
|
++// Check if client exists
|
|
|
|
|
++const checkClientExists = async (token, clientId) => !!await getClientByClientId(token, clientId);
|
|
|
|
|
+
|
|
|
|
|
++// Get client mappers by client ID
|
|
|
|
|
+ async function getClientMappers(token, clientId) {
|
|
|
|
|
++ console.log(`Getting client mappers for ${clientId}...`);
|
|
|
|
|
++ const client = await getClientByClientId(token, clientId);
|
|
|
|
|
++ if (!client) {
|
|
|
|
|
++ console.log(`Client ${clientId} not found, no mappers to get.`);
|
|
|
|
|
++ return [];
|
|
|
|
|
++ }
|
|
|
|
|
+ try {
|
|
|
|
|
+- const client = await getClient(token, clientId);
|
|
|
|
|
+- if (!client) {
|
|
|
|
|
+- return [];
|
|
|
|
|
+- }
|
|
|
|
|
+ const response = await axios.get(
|
|
|
|
|
+ `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/protocol-mappers/models`,
|
|
|
|
|
+- {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Authorization': `Bearer ${token}`
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
++ { headers: { 'Authorization': `Bearer ${token}` } }
|
|
|
|
|
+ );
|
|
|
|
|
++ console.log(`Client mappers for ${clientId} retrieved.`);
|
|
|
|
|
+ return response.data;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+- handleAxiosError(error, `getting client mappers for ${clientId}`, error.config, error.response);
|
|
|
|
|
++ handleAxiosError(error, `getting client mappers for ${clientId}`, error.config, error.response);
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-async function getClientScopes(token, clientId){
|
|
|
|
|
++// Get client scopes for a client
|
|
|
|
|
++async function getClientScopes(token, clientId) {
|
|
|
|
|
++ console.log(`Getting client scopes for ${clientId}...`);
|
|
|
|
|
++ const client = await getClientByClientId(token, clientId);
|
|
|
|
|
++ if (!client) {
|
|
|
|
|
++ console.log(`Client ${clientId} not found, no client scopes to get.`);
|
|
|
|
|
++ return [];
|
|
|
|
|
++ }
|
|
|
|
|
+ try {
|
|
|
|
|
+- const client = await getClient(token, clientId);
|
|
|
|
|
+- if(!client)
|
|
|
|
|
+- return [];
|
|
|
|
|
+-
|
|
|
|
|
+ const response = await axios.get(
|
|
|
|
|
+ `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/client-scopes`,
|
|
|
|
|
+- {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Authorization': `Bearer ${token}`
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
++ { headers: { 'Authorization': `Bearer ${token}` } }
|
|
|
|
|
+ );
|
|
|
|
|
+-
|
|
|
|
|
++ console.log(`Client scopes for ${clientId} retrieved.`);
|
|
|
|
|
+ return response.data;
|
|
|
|
|
+-
|
|
|
|
|
+- } catch(error){
|
|
|
|
|
+- handleAxiosError(error, `getting client scopes for ${clientId}`, error.config, error.response);
|
|
|
|
|
++ } catch (error) {
|
|
|
|
|
++ handleAxiosError(error, `getting client scopes for ${clientId}`, error.config, error.response);
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
++// Get a specific client scope by name
|
|
|
|
|
+ async function getClientScope(token, scopeName) {
|
|
|
|
|
++ console.log(`Getting client scope "${scopeName}"...`);
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await axios.get(
|
|
|
|
|
+ `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes`,
|
|
|
|
|
+- {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Authorization': `Bearer ${token}`
|
|
|
|
|
+- },
|
|
|
|
|
+- params: {
|
|
|
|
|
+- name: scopeName
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
++ { headers: { 'Authorization': `Bearer ${token}` } }
|
|
|
|
|
+ );
|
|
|
|
|
+-
|
|
|
|
|
+- if(response.data.length === 0){
|
|
|
|
|
+- console.error(`Client Scope ${scopeName} not found`);
|
|
|
|
|
++ const foundScope = response.data.find(scope => scope.name === scopeName);
|
|
|
|
|
++ if (!foundScope) {
|
|
|
|
|
++ console.log(`Client Scope "${scopeName}" not found`);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+-
|
|
|
|
|
+- return response.data[0]
|
|
|
|
|
+- } catch (error){
|
|
|
|
|
++ console.log(`Client scope "${scopeName}" found:`, foundScope);
|
|
|
|
|
++ return foundScope;
|
|
|
|
|
++ } catch (error) {
|
|
|
|
|
+ handleAxiosError(error, `getting client scope ${scopeName}`, error.config, error.response);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-async function addDefaultClientScope(token, clientId, scopeName){
|
|
|
|
|
++// Add a default client scope to a client
|
|
|
|
|
++async function addDefaultClientScope(token, clientId, scopeName) {
|
|
|
|
|
++ console.log(`Adding client scope "${scopeName}" as default for client "${clientId}"...`);
|
|
|
|
|
++ const client = await getClientByClientId(token, clientId);
|
|
|
|
|
++ const scope = await getClientScope(token, scopeName);
|
|
|
|
|
++ if (!client || !scope) {
|
|
|
|
|
++ console.log(`Client "${clientId}" or scope "${scopeName}" not found, cannot add as default scope.`);
|
|
|
|
|
++ return;
|
|
|
|
|
++ }
|
|
|
|
|
+ try {
|
|
|
|
|
+- const client = await getClient(token, clientId);
|
|
|
|
|
+- const scope = await getClientScope(token, scopeName);
|
|
|
|
|
+- if(!client || !scope){
|
|
|
|
|
+- return null;
|
|
|
|
|
+- }
|
|
|
|
|
+-
|
|
|
|
|
+-
|
|
|
|
|
+ await axios.put(
|
|
|
|
|
+- `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/default-client-scopes/${scope.id}`,
|
|
|
|
|
+- null,
|
|
|
|
|
+- {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Authorization': `Bearer ${token}`,
|
|
|
|
|
+- 'Content-Type': 'application/json'
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
++ `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/default-client-scopes/${scope.id}`,
|
|
|
|
|
++ null,
|
|
|
|
|
++ {
|
|
|
|
|
++ headers: {
|
|
|
|
|
++ 'Authorization': `Bearer ${token}`,
|
|
|
|
|
++ 'Content-Type': 'application/json'
|
|
|
|
|
++ }
|
|
|
|
|
++ }
|
|
|
|
|
+ );
|
|
|
|
|
+-
|
|
|
|
|
+- console.log(`Client scope ${scopeName} added as default scope for client ${clientId}`)
|
|
|
|
|
+-
|
|
|
|
|
+- } catch(error){
|
|
|
|
|
+- handleAxiosError(error, `adding client scope ${scopeName} as default for client ${clientId}`);
|
|
|
|
|
++ console.log(`Client scope "${scopeName}" added as default scope for client "${clientId}"`);
|
|
|
|
|
++ } catch (error) {
|
|
|
|
|
++ handleAxiosError(error, `adding client scope "${scopeName}" as default for client "${clientId}"`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-
|
|
|
|
|
+-// Realm erstellen
|
|
|
|
|
++// Create Realm
|
|
|
|
|
+ async function createRealm(token) {
|
|
|
|
|
++ console.log(`Creating realm ${REALM_NAME}...`);
|
|
|
|
|
+ const realmConfig = {
|
|
|
|
|
+ realm: REALM_NAME,
|
|
|
|
|
+ enabled: true,
|
|
|
|
|
+@@ -250,67 +241,47 @@ async function createRealm(token) {
|
|
|
|
|
+ webAuthnPolicyUserVerificationRequirement: "preferred",
|
|
|
|
|
+ webAuthnPolicyCreateTimeout: 0,
|
|
|
|
|
+ webAuthnPolicyAvoidSameAuthenticatorRegister: false,
|
|
|
|
|
+- defaultDefaultClientScopes: [
|
|
|
|
|
+- "email",
|
|
|
|
|
+- "profile",
|
|
|
|
|
+- "roles",
|
|
|
|
|
+- "web-origins"
|
|
|
|
|
+- ],
|
|
|
|
|
+- defaultOptionalClientScopes: [
|
|
|
|
|
+- "address",
|
|
|
|
|
+- "phone",
|
|
|
|
|
+- "offline_access",
|
|
|
|
|
+- "microprofile-jwt"
|
|
|
|
|
+- ]
|
|
|
|
|
++ defaultDefaultClientScopes: ["email", "profile", "roles", "web-origins"],
|
|
|
|
|
++ defaultOptionalClientScopes: ["address", "phone", "offline_access", "microprofile-jwt"]
|
|
|
|
|
+ };
|
|
|
|
|
+-
|
|
|
|
|
+ try {
|
|
|
|
|
+- await axios.post(
|
|
|
|
|
+- `${KEYCLOAK_URL}/admin/realms`,
|
|
|
|
|
+- realmConfig,
|
|
|
|
|
+- {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Authorization': `Bearer ${token}`,
|
|
|
|
|
+- 'Content-Type': 'application/json'
|
|
|
|
|
+- }
|
|
|
|
|
++ await axios.post(`${KEYCLOAK_URL}/admin/realms`, realmConfig, {
|
|
|
|
|
++ headers: {
|
|
|
|
|
++ 'Authorization': `Bearer ${token}`,
|
|
|
|
|
++ 'Content-Type': 'application/json'
|
|
|
|
|
+ }
|
|
|
|
|
+- );
|
|
|
|
|
++ });
|
|
|
|
|
+ console.log('Realm created successfully');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ handleAxiosError(error, 'creating realm');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-// Client erstellen
|
|
|
|
|
++// Create client and manage mappers
|
|
|
|
|
+ async function createClient(token, clientId, clientName, redirectUris) {
|
|
|
|
|
+- let client;
|
|
|
|
|
+- const clientExists = await checkClientExists(token, clientId);
|
|
|
|
|
++ console.log(`Creating client "${clientId}"...`);
|
|
|
|
|
++ let client = await getClientByClientId(token, clientId);
|
|
|
|
|
+
|
|
|
|
|
+- if (!clientExists) {
|
|
|
|
|
++ if (!client) {
|
|
|
|
|
+ const clientConfig = {
|
|
|
|
|
+ clientId: clientId,
|
|
|
|
|
+ name: clientName,
|
|
|
|
|
+ enabled: true,
|
|
|
|
|
+ protocol: "openid-connect",
|
|
|
|
|
+ publicClient: false,
|
|
|
|
|
+- authorizationServicesEnabled: true,
|
|
|
|
|
+- serviceAccountsEnabled: true,
|
|
|
|
|
++ authorizationServicesEnabled: false,
|
|
|
|
|
++ serviceAccountsEnabled: false,
|
|
|
|
|
+ standardFlowEnabled: true,
|
|
|
|
|
+ implicitFlowEnabled: false,
|
|
|
|
|
+ directAccessGrantsEnabled: true,
|
|
|
|
|
+ redirectUris: redirectUris,
|
|
|
|
|
+- webOrigins: ["+"],
|
|
|
|
|
+- defaultClientScopes: [
|
|
|
|
|
+- "roles"
|
|
|
|
|
+- ],
|
|
|
|
|
+- optionalClientScopes: [
|
|
|
|
|
+- "address",
|
|
|
|
|
+- "phone",
|
|
|
|
|
+- "offline_access",
|
|
|
|
|
+- "microprofile-jwt"
|
|
|
|
|
+- ]
|
|
|
|
|
++ webOrigins: ["+"],
|
|
|
|
|
++ defaultClientScopes: ["roles"],
|
|
|
|
|
++ optionalClientScopes: ["address", "phone", "offline_access", "microprofile-jwt"],
|
|
|
|
|
++ rootUrl: process.env.NEXTCLOUD_URL,
|
|
|
|
|
++ baseUrl: process.env.NEXTCLOUD_URL,
|
|
|
|
|
++ adminUrl: process.env.NEXTCLOUD_URL,
|
|
|
|
|
+ };
|
|
|
|
|
+-
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await axios.post(
|
|
|
|
|
+ `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients`,
|
|
|
|
|
+@@ -322,21 +293,34 @@ async function createClient(token, clientId, clientName, redirectUris) {
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+- console.log(`Client ${clientId} created successfully`);
|
|
|
|
|
++ console.log(`Client "${clientId}" created successfully`);
|
|
|
|
|
+ client = response.data;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ handleAxiosError(error, `creating client: ${clientId}`);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+- client = await getClient(token, clientId);
|
|
|
|
|
+- console.log(`Client ${clientId} already exists, checking mappers`);
|
|
|
|
|
++ console.log(`Client "${clientId}" already exists, checking mappers`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+- if (client) {
|
|
|
|
|
+-
|
|
|
|
|
+- const existingMappers = await getClientMappers(token, clientId)
|
|
|
|
|
++ if (client) {
|
|
|
|
|
++ try {
|
|
|
|
|
++ await axios.put(
|
|
|
|
|
++ `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
|
|
|
|
|
++ { ...client, secret: process.env[`KEYCLOAK_${clientId.replace(/[^a-zA-Z0-9]/g, '').toUpperCase()}_CLIENT_SECRET`] },
|
|
|
|
|
++ {
|
|
|
|
|
++ headers: {
|
|
|
|
|
++ 'Authorization': `Bearer ${token}`,
|
|
|
|
|
++ 'Content-Type': 'application/json'
|
|
|
|
|
++ }
|
|
|
|
|
++ }
|
|
|
|
|
++ );
|
|
|
|
|
++ console.log(`Set client secret for client: ${clientId}`);
|
|
|
|
|
++ } catch (error) {
|
|
|
|
|
++ handleAxiosError(error, `setting client secret for client: ${clientId}`, error.config, error.response);
|
|
|
|
|
++ }
|
|
|
|
|
+
|
|
|
|
|
++ const existingMappers = await getClientMappers(token, clientId);
|
|
|
|
|
+ const requiredMappers = [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "groups",
|
|
|
|
|
+@@ -367,10 +351,8 @@ async function createClient(token, clientId, clientName, redirectUris) {
|
|
|
|
|
+
|
|
|
|
|
+ for (const mapper of requiredMappers) {
|
|
|
|
|
+ const existingMapper = existingMappers.find(m => m.name === mapper.name);
|
|
|
|
|
+-
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (existingMapper) {
|
|
|
|
|
+- // Update existierenden Mapper
|
|
|
|
|
+ await axios.put(
|
|
|
|
|
+ `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/protocol-mappers/models/${existingMapper.id}`,
|
|
|
|
|
+ { ...existingMapper, ...mapper },
|
|
|
|
|
+@@ -381,9 +363,8 @@ async function createClient(token, clientId, clientName, redirectUris) {
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+- console.log(`Mapper ${mapper.name} updated for client ${clientId}`);
|
|
|
|
|
++ console.log(`Mapper "${mapper.name}" updated for client "${clientId}"`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+- // Erstelle neuen Mapper
|
|
|
|
|
+ await axios.post(
|
|
|
|
|
+ `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}/protocol-mappers/models`,
|
|
|
|
|
+ mapper,
|
|
|
|
|
+@@ -394,70 +375,69 @@ async function createClient(token, clientId, clientName, redirectUris) {
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
|
|
+- console.log(`Mapper ${mapper.name} created for client ${clientId}`);
|
|
|
|
|
++ console.log(`Mapper "${mapper.name}" created for client "${clientId}"`);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+- handleAxiosError(error, `managing mapper ${mapper.name} for client ${clientId}`, error.config, error.response);
|
|
|
|
|
+- // Wir werfen den Fehler nicht weiter, damit andere Mapper noch verarbeitet werden können
|
|
|
|
|
++ handleAxiosError(error, `managing mapper "${mapper.name}" for client "${clientId}"`, error.config, error.response);
|
|
|
|
|
++ }
|
|
|
|
|
++ }
|
|
|
|
|
++
|
|
|
|
|
++ if (clientId.includes("nextcloud")) {
|
|
|
|
|
++ await addDefaultClientScope(token, clientId, "openid");
|
|
|
|
|
++ await addDefaultClientScope(token, clientId, "profile");
|
|
|
|
|
++ await addDefaultClientScope(token, clientId, "groups-nextcloud");
|
|
|
|
|
++
|
|
|
|
|
++ try {
|
|
|
|
|
++ await axios.put(
|
|
|
|
|
++ `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${client.id}`,
|
|
|
|
|
++ { ...client, defaultClientScopes: client.defaultClientScopes.filter(c => c !== "nextcloud-dedicated") },
|
|
|
|
|
++ {
|
|
|
|
|
++ headers: {
|
|
|
|
|
++ 'Authorization': `Bearer ${token}`,
|
|
|
|
|
++ 'Content-Type': 'application/json'
|
|
|
|
|
++ }
|
|
|
|
|
++ }
|
|
|
|
|
++ );
|
|
|
|
|
++ console.log(`Removed client scope nextcloud-dedicated from client: ${clientId}`);
|
|
|
|
|
++ } catch (error) {
|
|
|
|
|
++ handleAxiosError(error, `removing client scope nextcloud-dedicated from client: ${clientId}`, error.config, error.response);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-
|
|
|
|
|
+-// Gruppen erstellen
|
|
|
|
|
++// Create default groups
|
|
|
|
|
+ async function createDefaultGroups(token) {
|
|
|
|
|
++ console.log('Creating default groups...');
|
|
|
|
|
+ const groups = [
|
|
|
|
|
+- {
|
|
|
|
|
+- name: "Administrators",
|
|
|
|
|
+- path: "/Administrators",
|
|
|
|
|
+- attributes: {
|
|
|
|
|
+- "description": ["Full system access"]
|
|
|
|
|
+- }
|
|
|
|
|
+- },
|
|
|
|
|
+- {
|
|
|
|
|
+- name: "Users",
|
|
|
|
|
+- path: "/Users",
|
|
|
|
|
+- attributes: {
|
|
|
|
|
+- "description": ["Regular system users"]
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
++ { name: "nextcloud-admins", path: "/nextcloud-admins", attributes: { "description": ["Nextcloud administrators"] } },
|
|
|
|
|
++ { name: "nextcloud-users", path: "/nextcloud-users", attributes: { "description": ["Nextcloud regular users"] } },
|
|
|
|
|
++ { name: "nextcloud-youpi", path: "/nextcloud-youpi", attributes: { "description": ["Nextcloud youpi"] } },
|
|
|
|
|
++ { name: "nextcloud-service", path: "/nextcloud-service", attributes: { "description": ["Nextcloud service user"] } }
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ for (const group of groups) {
|
|
|
|
|
+ try {
|
|
|
|
|
+- // Prüfen ob Gruppe existiert
|
|
|
|
|
+- const existingGroup = await axios.get(
|
|
|
|
|
+- `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/groups`,
|
|
|
|
|
+- {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Authorization': `Bearer ${token}`
|
|
|
|
|
+- },
|
|
|
|
|
+- params: {
|
|
|
|
|
+- search: group.name,
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
+- );
|
|
|
|
|
++ const existingGroups = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/groups`, {
|
|
|
|
|
++ headers: { 'Authorization': `Bearer ${token}` },
|
|
|
|
|
++ params: { search: group.name, exact: true } // Added exact: true for precise matching
|
|
|
|
|
++ });
|
|
|
|
|
+
|
|
|
|
|
+- if (existingGroup.data.length > 0) {
|
|
|
|
|
+- console.log(`Group ${group.name} already exists`);
|
|
|
|
|
++ if (existingGroups.data.length > 0) {
|
|
|
|
|
++ console.log(`Group "${group.name}" already exists`);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+- await axios.post(
|
|
|
|
|
+- `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/groups`,
|
|
|
|
|
+- group,
|
|
|
|
|
+- {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Authorization': `Bearer ${token}`,
|
|
|
|
|
+- 'Content-Type': 'application/json'
|
|
|
|
|
+- }
|
|
|
|
|
++ await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/groups`, group, {
|
|
|
|
|
++ headers: {
|
|
|
|
|
++ 'Authorization': `Bearer ${token}`,
|
|
|
|
|
++ 'Content-Type': 'application/json'
|
|
|
|
|
+ }
|
|
|
|
|
+- );
|
|
|
|
|
+- console.log(`Group ${group.name} created successfully`);
|
|
|
|
|
++ });
|
|
|
|
|
++ console.log(`Group "${group.name}" created successfully`);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ if (error.response?.status === 409) {
|
|
|
|
|
+- console.log(`Group ${group.name} already exists`);
|
|
|
|
|
++ console.log(`Group "${group.name}" already exists`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ handleAxiosError(error, `creating group: ${group.name}`);
|
|
|
|
|
+ }
|
|
|
|
|
+@@ -465,44 +445,44 @@ async function createDefaultGroups(token) {
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-
|
|
|
|
|
++// Create a test token for a user
|
|
|
|
|
+ async function createTestToken(token, username) {
|
|
|
|
|
++ console.log(`Creating test token for user "${username}"...`);
|
|
|
|
|
+ try {
|
|
|
|
|
+- const nextcloudClientId = Object.keys(CLIENTS).find(key => key.includes('nextcloud')) || 'nextcloud';
|
|
|
|
|
+- const client = await getClient(token, nextcloudClientId);
|
|
|
|
|
++ const nextcloudClientId = Object.keys(CLIENTS).find(key => key.includes('nextcloud')) || 'nextcloud';
|
|
|
|
|
++ const client = await getClientByClientId(token, nextcloudClientId);
|
|
|
|
|
+
|
|
|
|
|
+- if (!client)
|
|
|
|
|
++ if (!client) {
|
|
|
|
|
++ console.log(`Client "${nextcloudClientId}" not found, cannot create test token.`);
|
|
|
|
|
+ return null;
|
|
|
|
|
++ }
|
|
|
|
|
+ const response = await axios.post(
|
|
|
|
|
+ `${KEYCLOAK_URL}/realms/${REALM_NAME}/protocol/openid-connect/token`,
|
|
|
|
|
+ new URLSearchParams({
|
|
|
|
|
+ 'client_id': nextcloudClientId,
|
|
|
|
|
+- 'client_secret': process.env.KEYCLOAK_NEXTCLOUD_CLIENT_SECRET,
|
|
|
|
|
++ 'client_secret': process.env[`KEYCLOAK_${nextcloudClientId.replace(/[^a-zA-Z0-9]/g, '').toUpperCase()}_CLIENT_SECRET`],
|
|
|
|
|
+ 'username': username,
|
|
|
|
|
+ 'password': process.env.TESTADMIN_PASSWORD || "initial123!",
|
|
|
|
|
+ 'grant_type': 'password'
|
|
|
|
|
+ }),
|
|
|
|
|
+- {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Content-Type': 'application/x-www-form-urlencoded'
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
++ { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
|
|
|
|
|
+ );
|
|
|
|
|
++ console.log(`Test token for user "${username}" created.`);
|
|
|
|
|
+ return response.data.access_token;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ handleAxiosError(error, `getting test token for ${username}`, error.config, error.response);
|
|
|
|
|
++ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-// Funktion zum Decodieren eines JWT-Tokens
|
|
|
|
|
++// Function to decode a JWT token
|
|
|
|
|
+ function decodeToken(token) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const base64Url = token.split('.')[1];
|
|
|
|
|
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
|
|
|
+- const jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
|
|
|
|
|
+- return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
|
|
|
+- }).join(''));
|
|
|
|
|
+-
|
|
|
|
|
++ const jsonPayload = decodeURIComponent(atob(base64).split('').map(c =>
|
|
|
|
|
++ '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
|
|
|
|
|
++ ).join(''));
|
|
|
|
|
+ return JSON.parse(jsonPayload);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error("Error decoding token:", error.message);
|
|
|
|
|
+@@ -510,87 +490,128 @@ function decodeToken(token) {
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-// Test-User erstellen
|
|
|
|
|
++// Create initial users
|
|
|
|
|
+ async function createInitialUsers(token) {
|
|
|
|
|
++ console.log('Creating initial users...');
|
|
|
|
|
+ const users = [
|
|
|
|
|
+- {
|
|
|
|
|
+- username: "testadmin",
|
|
|
|
|
+- enabled: true,
|
|
|
|
|
+- emailVerified: true,
|
|
|
|
|
+- firstName: "Test",
|
|
|
|
|
+- lastName: "Admin",
|
|
|
|
|
+- email: "testadmin@mrx8086.com",
|
|
|
|
|
+- credentials: [{
|
|
|
|
|
+- type: "password",
|
|
|
|
|
+- value: process.env.TESTADMIN_PASSWORD || "initial123!",
|
|
|
|
|
+- temporary: true
|
|
|
|
|
+- }],
|
|
|
|
|
+- groups: ["/Administrators"]
|
|
|
|
|
+- },
|
|
|
|
|
+- {
|
|
|
|
|
+- username: "testuser",
|
|
|
|
|
+- enabled: true,
|
|
|
|
|
+- emailVerified: true,
|
|
|
|
|
+- firstName: "Test",
|
|
|
|
|
+- lastName: "User",
|
|
|
|
|
+- email: "testuser@mrx8086.com",
|
|
|
|
|
+- credentials: [{
|
|
|
|
|
+- type: "password",
|
|
|
|
|
+- value: process.env.TESTUSER_PASSWORD || "initial123!",
|
|
|
|
|
+- temporary: true
|
|
|
|
|
+- }],
|
|
|
|
|
+- groups: ["/Users"]
|
|
|
|
|
+- }
|
|
|
|
|
++ { username: "testadmin", enabled: true, emailVerified: true, firstName: "Test", lastName: "Admin", email: "testadmin@mrx8086.com", credentials: [{ type: "password", value: process.env.TESTADMIN_PASSWORD || "initial123!", temporary: false }], groups: ["nextcloud-admins", "nextcloud-users"] },
|
|
|
|
|
++ { username: "testuser", enabled: true, emailVerified: true, firstName: "Test", lastName: "User", email: "testuser@mrx8086.com", credentials: [{ type: "password", value: process.env.TESTUSER_PASSWORD || "initial123!", temporary: false }], groups: ["nextcloud-users", "nextcloud-youpi"] },
|
|
|
|
|
++ { username: "testserviceuser", enabled: true, emailVerified: true, firstName: "Test", lastName: "Service User", email: "testserviceuser@mrx8086.com", credentials: [{ type: "password", value: process.env.TESTSERVICEUSER_PASSWORD || "initial123!", temporary: false }], groups: ["nextcloud-service"] }
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ for (const user of users) {
|
|
|
|
|
+ try {
|
|
|
|
|
+- // Prüfen ob User existiert
|
|
|
|
|
+- const existingUsers = await axios.get(
|
|
|
|
|
+- `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users`,
|
|
|
|
|
+- {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Authorization': `Bearer ${token}`
|
|
|
|
|
+- },
|
|
|
|
|
+- params: {
|
|
|
|
|
+- username: user.username,
|
|
|
|
|
+- exact: true
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
+- );
|
|
|
|
|
++ const existingUsers = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users`, {
|
|
|
|
|
++ headers: { 'Authorization': `Bearer ${token}` },
|
|
|
|
|
++ params: { username: user.username, exact: true } // Added exact: true for precise matching
|
|
|
|
|
++ });
|
|
|
|
|
+
|
|
|
|
|
+ if (existingUsers.data.length > 0) {
|
|
|
|
|
+- console.log(`User ${user.username} already exists`);
|
|
|
|
|
++ console.log(`User "${user.username}" already exists`);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+- // User erstellen
|
|
|
|
|
+- await axios.post(
|
|
|
|
|
+- `${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users`,
|
|
|
|
|
+- user,
|
|
|
|
|
++ await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users`, user, {
|
|
|
|
|
++ headers: {
|
|
|
|
|
++ 'Authorization': `Bearer ${token}`,
|
|
|
|
|
++ 'Content-Type': 'application/json'
|
|
|
|
|
++ }
|
|
|
|
|
++ });
|
|
|
|
|
++ console.log(`User "${user.username}" created successfully`);
|
|
|
|
|
++
|
|
|
|
|
++ } catch (error) {
|
|
|
|
|
++ handleAxiosError(error, `creating user: ${user.username}`, error.config, error.response);
|
|
|
|
|
++ }
|
|
|
|
|
++ }
|
|
|
|
|
++}
|
|
|
|
|
++
|
|
|
|
|
++async function createGroupsNextcloudScope(token) {
|
|
|
|
|
++ const scopeName = "groups-nextcloud";
|
|
|
|
|
++ const mapperName = "groups-mapper";
|
|
|
|
|
++ console.log(`Starting createGroupsNextcloudScope`);
|
|
|
|
|
++ let clientScope = await getClientScope(token, scopeName);
|
|
|
|
|
++
|
|
|
|
|
++ if (!clientScope) {
|
|
|
|
|
++ try {
|
|
|
|
|
++ console.log(`Creating client scope "${scopeName}"`);
|
|
|
|
|
++ clientScope = await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes`,
|
|
|
|
|
++ {
|
|
|
|
|
++ "name": scopeName,
|
|
|
|
|
++ "protocol": "openid-connect",
|
|
|
|
|
++ "description": "Provides access to user group information for Nextcloud.", // Hinzugefügt: Beschreibung
|
|
|
|
|
++ "attributes": {},
|
|
|
|
|
++ "consentScreenText": "Grant access to user groups in Nextcloud",
|
|
|
|
|
++ "includeInTokenScope": true
|
|
|
|
|
++ },
|
|
|
|
|
+ {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Authorization': `Bearer ${token}`,
|
|
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
|
|
+ }
|
|
|
|
|
+- }
|
|
|
|
|
+- );
|
|
|
|
|
+- console.log(`User ${user.username} created successfully`);
|
|
|
|
|
++ });
|
|
|
|
|
++ console.log(`Client scope "${scopeName}" created successfully`);
|
|
|
|
|
++ clientScope = response.data;
|
|
|
|
|
++ } catch (error) {
|
|
|
|
|
++ console.error(`Error creating client scope "${scopeName}":`, error);
|
|
|
|
|
++ handleAxiosError(error, `creating ${scopeName} client scope`, error.config, error.response);
|
|
|
|
|
++ return;
|
|
|
|
|
++ }
|
|
|
|
|
++ } else {
|
|
|
|
|
++ console.log(`Client scope "${scopeName}" exists, skipping creation`);
|
|
|
|
|
++ }
|
|
|
|
|
+
|
|
|
|
|
++ console.log("Client scope creation step finished");
|
|
|
|
|
++ if (clientScope) {
|
|
|
|
|
++ console.log(`Check for mapper "${mapperName}" in scope "${scopeName}"`);
|
|
|
|
|
++ try {
|
|
|
|
|
++ const mappersResponse = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes/${clientScope.id}/protocol-mappers/models`,
|
|
|
|
|
++ { headers: { 'Authorization': `Bearer ${token}` } }
|
|
|
|
|
++ );
|
|
|
|
|
++ if (!mappersResponse.data.find(m => m.name === mapperName)) {
|
|
|
|
|
++ try {
|
|
|
|
|
++ await axios.post(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/client-scopes/${clientScope.id}/protocol-mappers/models`,
|
|
|
|
|
++ {
|
|
|
|
|
++ "name": mapperName,
|
|
|
|
|
++ "protocol": "openid-connect",
|
|
|
|
|
++ "protocolMapper": "oidc-group-membership-mapper",
|
|
|
|
|
++ "config": {
|
|
|
|
|
++ "full.path": "false",
|
|
|
|
|
++ "id.token.claim": "false",
|
|
|
|
|
++ "access.token.claim": "true",
|
|
|
|
|
++ "userinfo.token.claim": "true",
|
|
|
|
|
++ "claim.name": "groups",
|
|
|
|
|
++ "add.to.introspection": "false"
|
|
|
|
|
++ }
|
|
|
|
|
++ },
|
|
|
|
|
++ {
|
|
|
|
|
++ headers: {
|
|
|
|
|
++ 'Authorization': `Bearer ${token}`,
|
|
|
|
|
++ 'Content-Type': 'application/json'
|
|
|
|
|
++ }
|
|
|
|
|
++ });
|
|
|
|
|
++ console.log(`Mapper "${mapperName}" created for client scope "${scopeName}"`);
|
|
|
|
|
++ } catch (error) {
|
|
|
|
|
++ console.error(`Error creating mapper "${mapperName}" for scope "${scopeName}":`, error);
|
|
|
|
|
++ handleAxiosError(error, `creating mapper for ${scopeName}`, error.config, error.response);
|
|
|
|
|
++ }
|
|
|
|
|
++ } else {
|
|
|
|
|
++ console.log(`Mapper "${mapperName}" exists in client scope "${scopeName}", skipping creation`);
|
|
|
|
|
++ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+- handleAxiosError(error, `creating user: ${user.username}`, error.config, error.response);
|
|
|
|
|
++ console.error("Error checking for mappers:", error);
|
|
|
|
|
++ handleAxiosError(error, `checking mappers for ${scopeName}`, error.config, error.response);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
++ console.log("Finished createGroupsNextcloudScope");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-
|
|
|
|
|
+-// Hauptfunktion
|
|
|
|
|
++// Main function
|
|
|
|
|
+ async function setupRealm() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ console.log('Starting Keycloak setup...');
|
|
|
|
|
+ const token = await getAdminToken();
|
|
|
|
|
+
|
|
|
|
|
+- // Prüfe ob Realm existiert
|
|
|
|
|
++ // Check if realm exists
|
|
|
|
|
+ const realmExists = await checkRealmExists(token);
|
|
|
|
|
+
|
|
|
|
|
+ if (!realmExists) {
|
|
|
|
|
+@@ -600,41 +621,37 @@ async function setupRealm() {
|
|
|
|
|
+ console.log('Realm already exists, skipping base setup');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+- // Clients erstellen
|
|
|
|
|
++ // Create client scope groups-nextcloud
|
|
|
|
|
++ await createGroupsNextcloudScope(token);
|
|
|
|
|
++
|
|
|
|
|
++ // Create clients
|
|
|
|
|
+ for (const clientId in CLIENTS) {
|
|
|
|
|
+ await createClient(token, clientId, clientId, CLIENTS[clientId].redirectUris);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+- const nextcloudClientId = Object.keys(CLIENTS).find(key => key.includes('nextcloud')) || 'nextcloud';
|
|
|
|
|
+- await addDefaultClientScope(token, nextcloudClientId, "openid");
|
|
|
|
|
+-
|
|
|
|
|
+- // Gruppen erstellen
|
|
|
|
|
++ // Create groups
|
|
|
|
|
+ await createDefaultGroups(token);
|
|
|
|
|
+
|
|
|
|
|
+- // Test User erstellen
|
|
|
|
|
++ // Create test users
|
|
|
|
|
+ await createInitialUsers(token);
|
|
|
|
|
+
|
|
|
|
|
+-
|
|
|
|
|
+- // Konfiguration des Office-Automation Realms mit Admin Token auslesen
|
|
|
|
|
++ // Read the configuration of the Office-Automation realm with Admin Token
|
|
|
|
|
+ if (token) {
|
|
|
|
|
+ console.log("Master Realm Admin Token:", token);
|
|
|
|
|
+-
|
|
|
|
|
+ try {
|
|
|
|
|
+ const realmConfig = await axios.get(`${KEYCLOAK_URL}/admin/realms/${REALM_NAME}`, {
|
|
|
|
|
+- headers: {
|
|
|
|
|
+- 'Authorization': `Bearer ${token}`
|
|
|
|
|
+- }
|
|
|
|
|
++ headers: { 'Authorization': `Bearer ${token}` }
|
|
|
|
|
+ });
|
|
|
|
|
+- console.log("Office Automation Realm Configuration:", realmConfig.data)
|
|
|
|
|
++ console.log("Office Automation Realm Configuration:", realmConfig.data);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ handleAxiosError(error, 'getting office realm configuration', error.config, error.response);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+- console.error("Error getting Master Realm admin token")
|
|
|
|
|
++ console.error("Error getting Master Realm admin token");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+- // Test Token erstellen
|
|
|
|
|
+- const testToken = await createTestToken(token, "testadmin@mrx8086.com");
|
|
|
|
|
++ // Create Test Token
|
|
|
|
|
++ const testToken = await createTestToken(token, "testadmin");
|
|
|
|
|
+
|
|
|
|
|
+ if (testToken) {
|
|
|
|
|
+ console.log("Test Token generated successfully!");
|
|
|
|
|
+@@ -644,7 +661,6 @@ async function setupRealm() {
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.error("Error generating Test Token");
|
|
|
|
|
+ }
|
|
|
|
|
+-
|
|
|
|
|
+ console.log('Setup completed successfully');
|
|
|
|
|
+
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+@@ -653,5 +669,5 @@ async function setupRealm() {
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+-// Script ausführen
|
|
|
|
|
++// Execute the script
|
|
|
|
|
+ setupRealm();
|
|
|
|
|
+\ No newline at end of file
|
|
|
|
|
+diff --git a/scripts/setup/keycloak/test_realm.js b/scripts/setup/keycloak/test_realm.js
|
|
|
|
|
+index 78c03a4..11e3254 100644
|
|
|
|
|
+--- a/scripts/setup/keycloak/test_realm.js
|
|
|
|
|
++++ b/scripts/setup/keycloak/test_realm.js
|
|
|
|
|
+@@ -6,7 +6,7 @@ dotenv.config();
|
|
|
|
|
+ const KEYCLOAK_URL = process.env.KEYCLOAK_URL || 'https://auth.mrx8086.com';
|
|
|
|
|
+ const NEXTCLOUD_CLIENT_ID = process.env.NEXTCLOUD_CLIENT_ID || 'nextcloud';
|
|
|
|
|
+ const TESTADMIN_USERNAME = "testadmin@mrx8086.com";
|
|
|
|
|
+-const TESTADMIN_PASSWORD = process.env.TESTADMIN_PASSWORD || "initial123!";
|
|
|
|
|
++const TESTADMIN_PASSWORD = process.env.TESTADMIN_PASSWORD;
|
|
|
|
|
+ const REALM_NAME = 'office-automation';
|
|
|
|
|
+ const CLIENT_SECRET = process.env.KEYCLOAK_NEXTCLOUD_CLIENT_SECRET;
|
|
|
|
|
+
|
|
|
|
|
+@@ -74,28 +74,33 @@ function decodeToken(token) {
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
++// Prüfe ob ein Admin Token korrekt generiert werden kann
|
|
|
|
|
+ async function testKeycloakLogin() {
|
|
|
|
|
+- try {
|
|
|
|
|
+- const accessToken = await getAccessToken(TESTADMIN_USERNAME, TESTADMIN_PASSWORD);
|
|
|
|
|
++ try {
|
|
|
|
|
++ const accessToken = await getAccessToken(TESTADMIN_USERNAME, TESTADMIN_PASSWORD);
|
|
|
|
|
+
|
|
|
|
|
+- if (!accessToken) {
|
|
|
|
|
+- console.error('Failed to get access token.');
|
|
|
|
|
+- return;
|
|
|
|
|
+- }
|
|
|
|
|
+- console.log('Access Token:', accessToken);
|
|
|
|
|
+- const decodedToken = decodeToken(accessToken);
|
|
|
|
|
+- if (decodedToken) {
|
|
|
|
|
+- console.log('Decoded Access Token:', decodedToken);
|
|
|
|
|
+- if (decodedToken.groups.includes('/Administrators')){
|
|
|
|
|
+- console.log("Admin Group is set correctly!")
|
|
|
|
|
+- } else {
|
|
|
|
|
+- console.error("Admin Group is not set correctly!")
|
|
|
|
|
+- }
|
|
|
|
|
+- }
|
|
|
|
|
++ if (!accessToken) {
|
|
|
|
|
++ console.error('Failed to get access token.');
|
|
|
|
|
++ return;
|
|
|
|
|
++ }
|
|
|
|
|
++ console.log('Access Token:', accessToken);
|
|
|
|
|
++ const decodedToken = decodeToken(accessToken);
|
|
|
|
|
++ if(decodedToken) {
|
|
|
|
|
++ console.log('Decoded Access Token:', decodedToken);
|
|
|
|
|
++ if (Array.isArray(decodedToken.groups) && decodedToken.groups.includes('/nextcloud-admins')){
|
|
|
|
|
++ console.log("Admin Group is set correctly!")
|
|
|
|
|
++ } else if (typeof decodedToken.groups === 'string' && decodedToken.groups.includes('/nextcloud-admins')) {
|
|
|
|
|
++ console.log("Admin Group is set correctly!")
|
|
|
|
|
++ }
|
|
|
|
|
++ else {
|
|
|
|
|
++ console.error("Admin Group is not set correctly!")
|
|
|
|
|
++ }
|
|
|
|
|
++ }
|
|
|
|
|
+
|
|
|
|
|
+- } catch (error) {
|
|
|
|
|
+- console.error('An error occurred:', error);
|
|
|
|
|
+- }
|
|
|
|
|
++
|
|
|
|
|
++ } catch (error) {
|
|
|
|
|
++ console.error('An error occurred:', error);
|
|
|
|
|
++ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ testKeycloakLogin();
|
|
|
|
|
+\ No newline at end of file
|