From b8ba1589bdf689ee5201bc7bddf622fa916f8db7 Mon Sep 17 00:00:00 2001
From: Christian Elberfeld <6413499+elberfeld@users.noreply.github.com>
Date: Fri, 31 May 2024 20:48:14 +0200
Subject: [PATCH] wordpress mit openid anbindung

---
 site.yml                                      |  24 ++-
 testserver/docker_wordpress/Documentation.md  |  94 +++++++++++
 testserver/docker_wordpress/tasks/main.yml    |  39 +++++
 .../docker_wordpress/templates/Dockerfile     |  10 ++
 .../templates/config/uploads.ini              |   6 +
 .../plugins/wz-status/wz-status.php           | 152 ++++++++++++++++++
 .../templates/docker-compose.yml              |  40 +++++
 webserver/docker_wordpress/tasks/main.yml     |  21 +--
 8 files changed, 371 insertions(+), 15 deletions(-)
 create mode 100644 testserver/docker_wordpress/Documentation.md
 create mode 100644 testserver/docker_wordpress/tasks/main.yml
 create mode 100644 testserver/docker_wordpress/templates/Dockerfile
 create mode 100644 testserver/docker_wordpress/templates/config/uploads.ini
 create mode 100644 testserver/docker_wordpress/templates/data/wp-content/plugins/wz-status/wz-status.php
 create mode 100644 testserver/docker_wordpress/templates/docker-compose.yml

diff --git a/site.yml b/site.yml
index 4a721d3c..3d9eb969 100644
--- a/site.yml
+++ b/site.yml
@@ -54,6 +54,12 @@
         basedir: "/srv/{{ servicename }}",
         domain: "verwaltung-git.test-warpzone.de"
       }
+    - { 
+        role: testserver/docker_grafana, tags: [ test_grafana, docker_services ],
+        servicename: "grafana",
+        basedir: "/srv/{{ servicename }}",
+        domain: "grafana.test-warpzone.de"
+      }
     - { 
         role: testserver/docker_nextcloud, tags: [ test_nextcloud, docker_services ],
         servicename: "nextcloud",
@@ -66,6 +72,18 @@
         basedir: "/srv/{{ servicename }}",
         domain: "tandoor.test-warpzone.de" 
       }
+    - { 
+        role: testserver/docker_vpnserver, tags: [ test_vpnserver, docker_services ],
+        servicename: "vpnserver",
+        basedir: "/srv/{{ servicename }}",
+        domain: "vpn.test-warpzone.de" 
+      }
+    - { 
+        role: testserver/docker_wordpress, tags: [ test_wordpress, docker_services ],
+        servicename: "wordpress",
+        basedir: "/srv/{{ servicename }}",
+        domain: "www.test-warpzone.de" 
+      }
 
 
 ##################################################
@@ -280,12 +298,6 @@
         basedir: /srv/matrix, 
         domain: "matrix.warpzone.ms"
       }
-    - { 
-        role: webserver/docker_vpnserver, tags: [ vpnserver, docker_services ],
-        servicename: "vpnserver",
-        basedir: /srv/vpnserver, 
-        domain: "vpn.warpzone.ms"
-      }
     - { 
         role: webserver/docker_warpapi, tags: [ warpapi, docker_services ],
         servicename: "warpapi",
diff --git a/testserver/docker_wordpress/Documentation.md b/testserver/docker_wordpress/Documentation.md
new file mode 100644
index 00000000..5ebd4b77
--- /dev/null
+++ b/testserver/docker_wordpress/Documentation.md
@@ -0,0 +1,94 @@
+
+# Overview 
+
+* Authentication to Wordpress is only possible with an account in uffd, regular authentication is disabled 
+* All users with group 'wordpress_access' can access Wordpress with 'Editor' privileges, the user in Wordpress is created on first login 
+* Users with group 'wordpress_admin' get 'Administrator' privigeges. 
+
+# Setup OIDC Authentication via uffd 
+
+Uffd Reference: https://git.cccv.de/uffd
+
+
+## Setup in Wordpress 
+
+Wordpress Plugin: OpenID Connect Generic Client
+https://de.wordpress.org/plugins/daggerhart-openid-connect-generic
+
+Pluin settings:
+* Anmeldetyp:
+* Client-ID: wordpress
+* Client Secret Key: <secret key>
+* OpenID Scope: openid profile email groups
+* Endpunkt-URL zur Anmeldung: https://uffd.test-warpzone.de/oauth2/authorize
+* Endpunkt-URL zur Benutzerinfo: https://uffd.test-warpzone.de/oauth2/userinfo
+* Endpunkt-URL zur Token-Verifizierung: https://uffd.test-warpzone.de/oauth2/token
+* Endpunkt-URL zum Sitzungsende: https://uffd.test-warpzone.de/oauth2/logout
+* Identitäts-Schlüssel: preferred_username
+* Spitznamen-Schlüssel: preferred_username
+* Email-Format: {email}
+* Anzeigename-Formatierung: {preferred_username}
+* Mit Benutzername identifizieren: Ja
+* State-Zeitbeschränkung: 180
+* Aktualisierungstoken aktivieren: Ja
+* Existierende Benutzer verknüpfen: Ja
+* Benutzer erstellen, wenn er nicht existiert: Ja
+* Zur ursprünglichen Seite zurückleiten: Nein
+* Zur Anmeldeansicht weiterleiten, wenn die Sitzung abgelaufen ist: Ja
+
+## Setup in uffd
+
+Create Groups:
+
+- wordpress_access: General Access to Nextcloud 
+- wordpress_admin: This Group will be Mapped to the Group admin in Wordpress 
+
+Create a Service / OAuth Client: 
+
+Only Users with goup nextcloud_access can access Nextcloud 
+
+Client-ID: wordpress 
+Client-Secret: from file nextcloud_oidc_secret on the server
+Redirect-URIs: 
+* https://www.test-warpzone.de/wp-admin/admin-ajax.php?action=openid-connect-authorize
+
+## Mapping von Rollen in Wordpress
+
+Wordpress Plugin: WPCode 
+https://de.wordpress.org/plugins/insert-headers-and-footers/
+
+Additional references: 
+https://github.com/oidc-wp/openid-connect-generic/issues/164
+
+Um die Wordpress Berechtigungen auf Basis der Gruppen in uffd zu steuern ist ein zusätzliches Code-Snippet erforderlich. 
+Dieses Code-Snippet kann am besten mit dem addon 'WPCode' verwaltet werden. 
+In dem Plugin muss das folgenden neues PHP-Snippet erstellt und aktiviert werden.
+
+Benutzer, mit der Gruppe 'wordpress_admin' in uddf erhalten Administrator-Berechtigungen. 
+Alle anderen Benutzer erhalten Editor-Berechtigungen. 
+
+```
+add_action('openid-connect-generic-update-user-using-current-claim', function( $user, $user_claim) {
+	
+    // Based on some data in the user_claim, modify the user.
+	foreach($user_claim as $key => $value) {
+		error_log('Openid Role mapping: User claim: ' . $key . ", Value: " . $value);
+	}
+	
+	if ( array_key_exists( 'groups', $user_claim ) ) {
+		
+		error_log('Openid Role mapping: Groups: ' . implode(',',$user_claim['groups']));
+
+		if ( in_array('wordpress_admin', $user_claim['groups'] )) {
+			
+			error_log('Openid Role mapping: Set role: Administrator');
+        	$user->set_role( 'administrator' );
+		}
+		else {
+			
+			error_log('Openid Role mapping: Set role: Editor');
+			$user->set_role( 'editor' );
+		}
+    }
+}, 10, 2);
+```
diff --git a/testserver/docker_wordpress/tasks/main.yml b/testserver/docker_wordpress/tasks/main.yml
new file mode 100644
index 00000000..46107a11
--- /dev/null
+++ b/testserver/docker_wordpress/tasks/main.yml
@@ -0,0 +1,39 @@
+---
+
+- include_tasks: ../functions/get_secret.yml
+  with_items:
+   - { path: "/srv/shared/noreply_email_pass", length: -1 } 
+   - { path: "{{ basedir }}/mysql_root_pass",  length: 24 }
+   - { path: "{{ basedir }}/mysql_user_pass",  length: 12 }
+   - { path: "{{ basedir }}/wordpress_admin_pass",  length: 24 }
+   - { path: "{{ basedir }}/wordpress_client_secret",  length: 32 }
+
+- name: create folder struct for wordpress
+  file:
+    path: "{{ item }}"
+    state: "directory"
+    owner: www-data
+    group: www-data
+  with_items:
+    - "{{ basedir }}/"
+    - "{{ basedir }}/db/"
+    - "{{ basedir }}/config"
+    - "{{ basedir }}/data/"
+    - "{{ basedir }}/data/wp-content/"
+    - "{{ basedir }}/data/wp-content/plugins/"
+    - "{{ basedir }}/data/wp-content/plugins/wz-status/"
+
+- name: create config file
+  template:
+    src: "{{ item }}"
+    dest: "{{ basedir }}/{{ item }}"
+  with_items:
+    - Dockerfile
+    - docker-compose.yml
+    - config/uploads.ini
+    - data/wp-content/plugins/wz-status/wz-status.php
+
+- name: start wordpress docker
+  community.docker.docker_compose_v2:
+    project_src: "{{ basedir }}"
+    state: present
diff --git a/testserver/docker_wordpress/templates/Dockerfile b/testserver/docker_wordpress/templates/Dockerfile
new file mode 100644
index 00000000..1c41da02
--- /dev/null
+++ b/testserver/docker_wordpress/templates/Dockerfile
@@ -0,0 +1,10 @@
+FROM wordpress:6.4.2-apache
+
+# install the PHP extensions we need
+RUN set -x \
+	&& apt-get update \
+	&& apt-get install -y libldap2-dev \
+	&& rm -rf /var/lib/apt/lists/* \
+	&& docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu/ \
+	&& docker-php-ext-install ldap \
+	&& apt-get purge -y --auto-remove libldap2-dev
diff --git a/testserver/docker_wordpress/templates/config/uploads.ini b/testserver/docker_wordpress/templates/config/uploads.ini
new file mode 100644
index 00000000..c32c80ec
--- /dev/null
+++ b/testserver/docker_wordpress/templates/config/uploads.ini
@@ -0,0 +1,6 @@
+
+file_uploads = On
+memory_limit = 64M
+upload_max_filesize = 64M
+post_max_size = 64M
+max_execution_time = 600
diff --git a/testserver/docker_wordpress/templates/data/wp-content/plugins/wz-status/wz-status.php b/testserver/docker_wordpress/templates/data/wp-content/plugins/wz-status/wz-status.php
new file mode 100644
index 00000000..36b3bf0a
--- /dev/null
+++ b/testserver/docker_wordpress/templates/data/wp-content/plugins/wz-status/wz-status.php
@@ -0,0 +1,152 @@
+<?php
+/*
+Plugin Name: WZ Status
+Plugin URI: http://www.warpzone.ms
+Description: This plugin adds a custom widget.
+Version: 1.0
+Author: Christian <void> Elberfeld
+Author URI: http://www.warpzone.ms
+License: GPL2
+Original Source: https://github.com/wpexplorer/my-widget-plugin
+*/
+
+// The widget class
+class WZ_Status_Widget extends WP_Widget {
+
+	// Main constructor
+	public function __construct() {
+		parent::__construct(
+			'wz_status_widget',
+			__( 'WZ Status Widget', 'text_domain' ),
+			array(
+				'customize_selective_refresh' => true,
+			)
+		);
+	}
+
+	// The widget form (for the backend )
+	public function form( $instance ) {
+
+		// Set widget defaults
+		$defaults = array(
+                        'title'      => '',
+			'api_url'    => '',
+		);
+
+		// Parse current settings with defaults
+		extract( wp_parse_args( ( array ) $instance, $defaults ) ); ?>
+
+		<?php // Widget Title ?>
+		<p>
+			<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( 'Widget Title', 'text_domain' ); ?></label>
+			<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
+		</p>
+
+		<?php // Api Url ?>
+		<p>
+			<label for="<?php echo esc_attr( $this->get_field_id( 'api_url' ) ); ?>"><?php _e( 'Api Url:', 'api_url' ); ?></label>
+			<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'api_url' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'api_url' ) ); ?>" type="text" value="<?php echo esc_attr( $text ); ?>" />
+		</p>
+
+
+	<?php }
+
+	// Update widget settings
+	public function update( $new_instance, $old_instance ) {
+		$instance = $old_instance;
+		$instance['title']    = isset( $new_instance['title'] ) ? wp_strip_all_tags( $new_instance['title'] ) : '';
+		$instance['api_url']  = isset( $new_instance['api_url'] ) ? wp_strip_all_tags( $new_instance['api_url'] ) : '';
+		return $instance;
+	}
+
+	// Display the widget
+	public function widget( $args, $instance ) {
+
+		extract( $args );
+
+		// Check the widget options
+		$title    = isset( $instance['title'] ) ? apply_filters( 'widget_title', $instance['title'] ) : '';
+		$api_url  = isset( $instance['api_url'] ) ? $instance['api_url'] : '';
+
+		$zone_status = "UNBEKANNT";
+        $zone_status_text = "Unbekannt";
+		$zone_status_color = "#000000";
+
+		// WordPress core before_widget hook (always include )
+		echo $before_widget;
+
+		// Display the widget
+		echo '<div class="widget-text wp_widget_plugin_box">';
+
+			// Display widget title if defined
+			if ( $title ) {
+				echo $before_title . $title . $after_title;
+			}
+
+
+			// Zone Status abrufen
+
+            $curl = curl_init();
+
+            curl_setopt_array($curl, array(
+                CURLOPT_URL => "https://api.warpzone.ms/statuswidget",
+				CURLOPT_RETURNTRANSFER => true,
+                CURLOPT_FOLLOWLOCATION => true,
+                CURLOPT_ENCODING => "",
+                CURLOPT_MAXREDIRS => 3,
+                CURLOPT_TIMEOUT => 5,
+                CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+                CURLOPT_CUSTOMREQUEST => "GET"
+            ));
+
+			$response = curl_exec($curl);
+			$err = curl_error($curl);
+
+            curl_close($curl);
+
+			if ($err) {
+
+				$zone_status = $err;
+
+			} else {
+
+				$responseObj = json_decode($response,true);
+				$zone_status = $responseObj['zone_door_status'];
+
+				if ($zone_status == "OPEN") {
+
+					$zone_status_text =  "Offen";
+					$zone_status_color = "#00cc00";
+				}
+
+                if ($zone_status == "CLOSED") {
+
+                    $zone_status_text =  "Geschlossen";
+                    $zone_status_color = "#cc0000";
+                }
+
+			}
+
+
+			// Anzeige Status im Widget
+			echo "<span style='font-weight: bold; color:" . $zone_status_color . ";'>" . $zone_status_text . "</span>";
+
+			// Status mit in die Menueleiste fuer Mobilgeraete
+			echo "<script type='text/javascript'>jQuery(document).ready(function() { jQuery('#mainnav-toggle').text('MENU | Status: " . $zone_status_text . "'); });</script>";
+
+
+		echo '</div>';
+
+		// WordPress core after_widget hook (always include )
+		echo $after_widget;
+
+	}
+
+}
+
+// Register the widget
+function my_register_wz_status_widget() {
+	register_widget( 'WZ_Status_Widget' );
+}
+add_action( 'widgets_init', 'my_register_wz_status_widget' );
+
diff --git a/testserver/docker_wordpress/templates/docker-compose.yml b/testserver/docker_wordpress/templates/docker-compose.yml
new file mode 100644
index 00000000..e0c093ac
--- /dev/null
+++ b/testserver/docker_wordpress/templates/docker-compose.yml
@@ -0,0 +1,40 @@
+services:
+
+  db:
+
+    image: mariadb:11
+    restart: always
+    volumes:
+      - /srv/wordpress/db/:/var/lib/mysql
+    environment:
+      MYSQL_ROOT_PASSWORD: "{{ mysql_root_pass }}"
+      MYSQL_PASSWORD: "{{ mysql_user_pass }}"
+      MYSQL_DATABASE: wordpress
+      MYSQL_USER: wordpress
+    networks:
+      - default
+
+  app:
+    # values set in configuration: noreply_email_user - noreply_email_pass - smtp_host - smtp_port 
+    build: .
+    restart: always
+    volumes:
+      - /srv/wordpress/config/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
+      - /srv/wordpress/data:/var/www/html
+    environment:
+      WORDPRESS_DB_HOST: db
+      WORDPRESS_DB_USER: wordpress
+      WORDPRESS_DB_PASSWORD: "{{ mysql_user_pass }}"
+    labels:
+      - com.centurylinklabs.watchtower.enable=false
+      - traefik.enable=true
+      - traefik.http.routers.{{ servicename }}.rule=Host(`{{ domain }}`)
+      - traefik.http.routers.{{ servicename }}.entrypoints=websecure
+      - traefik.http.services.{{ servicename }}.loadbalancer.server.port=80
+    networks:
+      - default
+      - web
+
+networks:
+  web:
+    external: true
diff --git a/webserver/docker_wordpress/tasks/main.yml b/webserver/docker_wordpress/tasks/main.yml
index 0f207950..995f4bce 100644
--- a/webserver/docker_wordpress/tasks/main.yml
+++ b/webserver/docker_wordpress/tasks/main.yml
@@ -2,9 +2,9 @@
 
 - include_tasks: ../functions/get_secret.yml
   with_items:
-   - { path: /srv/shared/noreply_email_pass, length: -1 } 
-   - { path: /srv/wordpress/mysql_root_pass,  length: 24 }
-   - { path: /srv/wordpress/mysql_user_pass,  length: 12 }
+   - { path: "/srv/shared/noreply_email_pass", length: -1 } 
+   - { path: "{{ basedir }}/mysql_root_pass",  length: 24 }
+   - { path: "{{ basedir }}/mysql_user_pass",  length: 12 }
 
 - name: create folder struct for wordpress
   file:
@@ -13,15 +13,18 @@
     owner: www-data
     group: www-data
   with_items:
-    - "/srv/wordpress/"
-    - "/srv/wordpress/config"
-    - "/srv/wordpress/data/"
-    - "/srv/wordpress/db/"
+    - "{{ basedir }}/"
+    - "{{ basedir }}/db/"
+    - "{{ basedir }}/config"
+    - "{{ basedir }}/data/"
+    - "{{ basedir }}/data/wp-content/"
+    - "{{ basedir }}/data/wp-content/plugins/"
+    - "{{ basedir }}/data/wp-content/plugins/wz-status/"
 
 - name: create config file
   template:
     src: "{{ item }}"
-    dest: "/srv/wordpress/{{ item }}"
+    dest: "{{ basedir }}/{{ item }}"
   with_items:
     - Dockerfile
     - docker-compose.yml
@@ -30,5 +33,5 @@
 
 - name: start wordpress docker
   community.docker.docker_compose_v2:
-    project_src: /srv/wordpress/
+    project_src: "{{ basedir }}"
     state: present
-- 
GitLab