Um das Benutzererlebnis der cloudbasierten IAM Lösung OneLogin komplett zu machen, muss irgendwann halt auch mal der Logout folgen.
Das hatte ich bisher nicht so wirklich auf dem Schirm, schließlich heißt es ja "OneLogin" und nicht "OneLogout".
Der Logout stellte uns erstmal vor große Probleme, denn irgendwie wollte kein Ansatz so richtig funktionieren. Irgendwann hatte ich dann aber die richtigen Informationsteile richtig zusammengepuzzelt und dann war es super einfach.
Konfiguration OneLogin
In der Konfiguration im OneLogin unter OIDC -> Configuration -> Post Logout Redirect URI setzen.
In der Applikation selbst muss eigentlich nichts konfiguriert werden.
Es muss lediglich ein Link aufgerufen werden.
In der Konfiguration des vorgeschalteten Reverse Proxies ist folgender Block für den OIDC-Connector:
#this is required by mod_auth_openidc
OIDCCryptoPassphrase <INSERT-HERE a-random-secret>
OIDCProviderMetadataURL <INSERT-HERE>
OIDCClientID <INSERT-HERE>
OIDCClientSecret <INSERT-HERE>
# OIDCRedirectURI is a vanity URL that must point to a path protected by this module but must NOT point to any content
OIDCRedirectURI https://www.meinetolleanwendung.de/private/redirect_uri
## OIDCScope params
## to put params including roles into header
OIDCScope "openid email profile groups params"
Wir benötigen die OIDCRedirectURI und fügen lediglich ein ?logout für den logout an, gefolgt von = https://www.meinetolleanwendung.de/logoutseite.html für den Redirect zur Post-Logoutseite.
Ich möchte den programmatischen Zugriff auf YouTube ausprobieren um ggf. später mit ein paar Ideen zu experimentieren.
Nach der ersten Erkenntnis, dass das gar nicht so gut dokumentiert ist, wie ich es erwartet hätte, folgt die zweite Erkenntnis, dass man dafür einen Api Key braucht.
Hier also die Notizen, wie ich meinen API Key angelegt habe.
Eine schöne Anleitung
Auf Kodi-Tipps gibt es eine schöne, bebilderte Anleitung, der ich hier folge.
Da dort eigentlich alles ausführlich beschrieben zu sein scheint, notiere ich hier nur knapp die Schritte, die ich ausführen werde.
Die Google Developer Console
Den YouTube API Key gibt es über Google und da ich bisher noch nicht viele Erfahrungen mit den Google Produkten als Entwickler gemacht habe, hier die erste wichtige URL zum Einstieg: Die Google Developer Console
Vor ewigen Zeiten hatte ich dort schon mal etwas getestet (Google Auth/Login in KeyCloak eingebunden) und daher ist schon ein Projekt vorhanden, in welchem ich direkt nach Seitenaufruf arbeite:
Natürlich möchte ich nicht in dem Projekt weiterarbeiten, sondern ein neues anlegen:
Meine Schritte in Bildern
User data vs Public data ? Ich bin mir nicht ganz sicher, was ich benötige. Ich brauche keine persönlichen Informationen, aber ich würde gerne zB die letzten angesehenen Videos meines YouTube Accounts abrufen können.
Starten wir mit Public data. Die Kodi Anleitung hat User data ausgewählt. Vermutlich werde ich später auch auf User data umsteigen müssen.
Public Data
Für Tests & Entwicklung richte ich keine Restrictions ein. Sollte mal irgendwann irgendwas irgendwie produktiv gehen, wird eh ein neuer API Key angelegt werden und der wird dann runter restrictiert so weit es geht.
Ganz klasse: Beim Testen des Keys erhalte ich immer "The request is missing a valid API key.". Toll. Könnte natürlich auch daran liegen, dass ich keine Ahnung habe, was ich da treibe und das falsch mache. Nützt alles nix, nächster Test mit Personal Data, das wird hoffentlich besser laufen.
Einen Hinweis habe ich noch gefunden, bracht mich aber erstmal nicht weiter:
Personal Data
Entweder finde ich den Einstieg von vorhin nicht mehr, oder durch Anlegen eines ersten API Keys hat sich die Oberfläche verändert und ich muss anders vorgehen.
Create geklickt, paar Sachen ausgefüllt, unsicher geworden, abgebrochen.
Nix besseres gefunden, später wieder auf "OAuth client ID" geklickt, dann sah es wieder anders aus, das ist dann die nächste Seite:
Erstmal mit einer Desktop App starten, später wird dann vielleicht mal eine Web App daraus.
Belohnt wird man dann mit einer OAuth Client-ID und Client-Secret. Sogar im JSON-Format, das ich mir abspeicher.
Ich habe dann noch ein paar Sachen getestet, aber irgendwie war der Wurm drin, nix ging.
Workaround: Projekt löschen, neu anfangen.
Was ein Spaß.
Zweiter Anlauf
Direkt mit User data weiter:
Scopes -> nix geändert -> "Save and continue"
Tja, leider kann ich aber keine User hinzufügen. Toll.
Nächster Anlauf:
Und noch einen API Key generieren:
Frust
Ich habe die Keys mit Postman und in Java Code ausprobiert, aber nichts hat funktioniert. Sehr frustrierend.
So wird das nichts. Ich brauche ein gutes Tutorial.
Die YouTube API Keys konnte ich anlegen, aber gebracht hat es mir nichts.
Der Tag danach
So ganz wollte ich doch nicht aufgeben und habe am Tag nach meinen YouTubeAPIGedöns Experimenten doch noch einen Versuch gewagt.
Über einen Stackoverflow Post habe ich ein Beispiel für eine API Query gefunden:
Als KEY habe ich meinen gestern generierten API Key genommen, als CHANNEL-ID habe ich aus meiner YouTube Abo Liste einen Link zu einem Kanal, und daraus den letzten Teil, genommen.
In meinem Beispiel https://www.youtube.com/channel/UC0RBJwg8ZfRM8TLGOKTpArA -> UC0RBJwg8ZfRM8TLGOKTpArA
Diese Informationen habe ich in Postman eingetragen und konnte endlich eine erfolgreiche Anfrage absenden:
In der Authorization Section hatte ich zuerst den Type auf No Auth zurückgestellt. Das scheint aber nicht erforderlich zu sein.
YAY!
Glück
Bei der Auswahl des Kanals hatte ich Glück!
Denn der Kanal verwendet eine Kanal-URL. Beispiel: youtube.com/channel/UCUZHFZ9jIKrLroW8LcyJEQQ
Viele andere Kanäle in meiner Abo Liste verwenden noch eine alte benutzerdefinierte URL. Beispiel: youtube.com/c/YouTubeCreators
Mit einer alten benutzerdefinierten URL funktioniert der Aufruf nämlich nicht, sondern nur mit der Kanal-URL.
We have a very, very old application that needs to be migrated into AWS. So we copied all files into AWS EC2 instance and tried to start the application. After fixing a lot of minor problems we faced a tough challenge with a SAPJCO RFC Call.
The Exception message was something like this:
Exception in thread "main" java.lang.ExceptionInInitializerError: JCO.classInitialize(): Could not load middleware layer 'com.sap.mw.jco.rfc.MiddlewareRFC'
JCO.nativeInit(): Could not initialize dynamic link library sapjcorfc [sapjcorfc (Not found in java.library.path)]. java.library.path [/usr/lib/jvm/java-1.6.0-ibm.x86_64/jre/lib/amd64/default:/usr/lib/jvm/java-1.6.0-ibm.x86_64/jre/lib/amd64:/usr/lib]
at com.sap.mw.jco.JCO.<clinit>(JCO.java:871)
at java.lang.J9VMInternals.initializeImpl(Native Method)
at java.lang.J9VMInternals.initialize(J9VMInternals.java:199)
I guess, with a JCO Version 3 we would not have much trouble, but in this ancient application JCO Version 2 is used and we cannot update to Version 3 without a huge efford. In other projects I had the luck that I could migrate to Version.
The application is running on a Linux system. But belive me: it would have been much harder on a Windows machine.
Analysis
To find the cause of the problem I wrote the simpliest JCO Test Programm I can image:
import com.sap.mw.jco.JCO;
public class TestMain {
public static void main(String[] args) {
System.out.println(JCO.getVersion());
}
}
Exception in thread "main" java.lang.ExceptionInInitializerError: JCO.classInitialize(): Could not load middleware layer 'com.sap.mw.jco.rfc.MiddlewareRFC'
JCO.nativeInit(): Could not initialize dynamic link library sapjcorfc [/app/JCo/libsapjcorfc.so: librfccm.so: cannot open shared object file: No such file or directory]. java.library.path [/app/JCo]
at com.sap.mw.jco.JCO.<clinit>(JCO.java:871)
at TestMain.main(TestMain.java:11)
Need to set an environment property first:
export LD_LIBRARY_PATH=/app/JCo
Run command line to start programm again and got another error:
Exception in thread "main" java.lang.ExceptionInInitializerError: JCO.classInitialize(): Could not load middleware layer 'com.sap.mw.jco.rfc.MiddlewareRFC'
JCO.nativeInit(): Could not initialize dynamic link library sapjcorfc [/app/JCo/libsapjcorfc.so: libstdc++.so.5: cannot open shared object file: No such file or directory]. java.library.path [/app/JCo]
at com.sap.mw.jco.JCO.<clinit>(JCO.java:871)
at TestMain.main(TestMain.java:11)
The interesting part of the error message:
Could not initialize dynamic link library sapjcorfc [/app/JCo/libsapjcorfc.so: libstdc++.so.5
Solution
We need the libstdc++.so.5 library, but installed is libstdc++.so.6
To get libstdc++.so.5 we installed package compat-libstdc++-33-3.2.3-66.x86_64:
yum install compat-libstdc++-33-3.2.3-66.x86_64
## to be honest, I am not exactly 100%% sure, what I did in my investigations, so the command may be a little differend, ex:
# yum install compat-libstdc++-33-3
# yum install compat-libstdc++-33
# yum install compat-libstdc++-33 libstdc++.so.5
Test
Run from command line:
java -cp ".:/app/JCo/sapjco.jar" TestMain
That gave me no error, but SAPJCo Version number.:
libstdc++.so.5 is a very old version of the standard c++ library.
Some Analysis Details
Writing this article is giving me the feeling, that this was all super easy. But in reality it was a real pain in the allerwertesten.
To isolate the source of the problem, I did not only write the small Java (JCO.getVersion) application, I also set up a Docker environment.
One challenge was to find a useful Docker image to start from. I started with an OpenJDK Image that was already deprecated. Deprecated was not the problem, but I could not install libstdc++.so.5.
Next I tried to use the newer, undeprecated Eclipse-Temurin Image. But still could not install libstdc++.so.5
So I finally ended in a Debian Image and self installed Java where I was able to install libstdc++5.
But there is one problem: You can only interact with a running container. But the TestMain-Programm is executed and immediately closed.
So I wrote another Test Programm, that keeps running, so I can enter the running container and test stuff (install packages, compile Java programm, etc.):
import java.io.BufferedReader;
import java.io.InputStreamReader;
import com.sap.mw.jco.JCO;
public class TestMain {
public static void main(String[] args) {
System.out.println("Hello World");
//System.out.println("JCO Version: " + JCO.getVersion());
while (true) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter Input : ");
try {
String s = br.readLine();
System.out.println(s);
}catch(Exception e) {
System.out.println(e);
}
}
}
}
Bei der Mutual SSL Authentication authentifizieren sich der Server und der Client gegenseitig.
PoC Setup
In diesem PoC authentifizieren sich der Load Balancer und der Server 1, bzw. Load Balancer und der Server 2, gegenseitig.
Der User kann durch den Load Balancer die Seite des Server 1, bzw. Server 2, aufrufen.
Ein direkter Aufruf der Seite auf den Servern, also ohne Load Balancer, ist nicht möglich, da nur der Load Balancer sich an den Servern 1&2 authentifizieren kann.
Zu Testzwecken wird allerdings Server 2 offen gehalten, eine Authentifizierung ist nicht erforderlich.
Keys und Zertifikate
Es wird eine eigene CA zum signieren der Zertifikate benötigt.
Für jeden Server wird ein Private Key und signierter Public Key benötigt.
Der LoadBalancer benötigt noch einen kombinierten Key aus seinem Private und Public Key.
Alle Dateien werden im Ordner certs abgelegt.
## Create CA
# Generate CA Key:
openssl genrsa -out ca-key.pem 2048
# Generate CA Certificate:
openssl req -x509 -new -nodes -extensions v3_ca -key ca-key.pem -days 1024 -out ca-root.pem -sha512 -subj "/C=DE/ST=NRW/L=Leverkusen/O=DerIngo/OU=IT Department/CN=deringo.de"
## Generate LoadBalancer
# Generate LoadBalancer Key:
openssl genrsa -out loadbalancer-key.pem 2048
## Generate CSR LoadBalancer
vim loadbalancer_csr_details.txt
openssl req -new -key loadbalancer-key.pem -out loadbalancer.csr -config loadbalancer_csr_details.txt
## Sign CSR
vim loadbalancer_ssl-extensions-x509.conf
openssl x509 -req -in loadbalancer.csr -CA ca-root.pem -CAkey ca-key.pem -CAcreateserial -out loadbalancer.crt -days 365 -sha512 -extensions v3_ca -extfile loadbalancer_ssl-extensions-x509.conf
## Mutual SSL - Reverse Proxy
## Create a file with the private and the signed public key:
# copy private key
cp loadbalancer-key.pem loadbalancer-combined.key
# copy public key (without public key of CA)
cat loadbalancer.crt >> loadbalancer-combined.key
## Add Public Key of CA to Public Key of RP
cat ca-root.pem >> loadbalancer.crt
## Generate Server_1
# Generate Server_1 Key:
openssl genrsa -out server_1-key.pem 2048
## Generate CSR Server_1
vim server_1_csr_details.txt
openssl req -new -key server_1-key.pem -out server_1.csr -config server_1_csr_details.txt
## Sign CSR
vim server_1_ssl-extensions-x509.conf
openssl x509 -req -in server_1.csr -CA ca-root.pem -CAkey ca-key.pem -CAcreateserial -out server_1.crt -days 365 -sha512 -extensions v3_ca -extfile server_1_ssl-extensions-x509.conf
## Add Public Key of CA to Public Key of RP
cat ca-root.pem >> server_1.crt
## Generate Server_2
# Generate Server_2 Key:
openssl genrsa -out server_2-key.pem 2048
## Generate CSR Server_2
vim server_2_csr_details.txt
openssl req -new -key server_2-key.pem -out server_2.csr -config server_2_csr_details.txt
## Sign CSR
vim server_2_ssl-extensions-x509.conf
openssl x509 -req -in server_2.csr -CA ca-root.pem -CAkey ca-key.pem -CAcreateserial -out server_2.crt -days 365 -sha512 -extensions v3_ca -extfile server_2_ssl-extensions-x509.conf
## Add Public Key of CA to Public Key of RP
cat ca-root.pem >> server_2.crt
Mutual SSL
LoadBalancer
Create a file with the private and the signed public key (bereits im Schritt zuvor erfolgt):
## Mutual SSL - Reverse Proxy
## Create a file with the private and the signed public key:
# copy private key
cp loadbalancer-key.pem loadbalancer-combined.key
# copy public key (without public key of CA)
cat loadbalancer.crt >> loadbalancer-combined.key
To communicate to the application server with SSL we need this lines in our HTTP configuration:
SSLProxyEngine On
SSLProxyMachineCertificateFile /usr/local/apache2/conf/combined.key
Der Fehler trat auf, nachdem ich meinen PoC von meinem Entwicklungsrechner in die Cloud transferierte und dort laufen ließ und dort testen wollte.
Auf der Console des Servers sieht der Aufruf mittels "curl http://localhost/private/index.html" auch erstmal gut aus.
Der Zugriff aus dem Browser mittels IP "http://<server-ip>/private/index.html" schlägt mit obiger Fehlermeldung komplett fehl.
Lokal nachstellen
Um das Problem lokal auf dem Entwicklerlaptop nachstellen zu können, muss erstmal die lokale (Docker-)IP ermittelt werden:
ping host.docker.internal
Ping wird ausgeführt für host.docker.internal [192.168.2.149] mit 32 Bytes Daten:
Antwort von 192.168.2.149: Bytes=32 Zeit<1ms TTL=128
Antwort von 192.168.2.149: Bytes=32 Zeit<1ms TTL=128
Antwort von 192.168.2.149: Bytes=32 Zeit<1ms TTL=128
Der Aufruf der PRIVATE Page, nach OneLogin Login versteht sich, funktioniert über localhost:
Der Aufruf der PRIVATE Page funktioniert nicht über die IP:
Auszug aus dem Logfile des Apache HTTP Servers:
reverseproxy_1 | [Thu Jun 09 13:00:54.048377 2022] [auth_openidc:error] [pid 256:tid 140563693147904] [client 172.24.0.1:33902] oidc_authenticate_user:
the URL hostname (localhost) of the configured OIDCRedirectURI does not match the URL hostname of the URL being accessed (192.168.2.149): the "state" a
nd "session" cookies will not be shared between the two!, referer: http://192.168.2.149/
reverseproxy_1 | 172.24.0.1 - - [09/Jun/2022:13:00:54 +0000] "GET /private/index.html HTTP/1.1" 500 531
Der Fehler besagt also, "the URL hostname (localhost) of the configured OIDCRedirectURI does not match the URL hostname of the URL being accessed (192.168.2.149)".
OneLogin Konfigurationsanpassung
Die Redirect URIs Liste muss um die IP des Servers erweitert werden:
Allerdings ist http als Protokoll lediglich für localhost zugelassen, nicht aber für alle anderen URIs, wie zB unsere Server IP oder später der Domain Name des Servers.
Die Redirect URI muss also mit https eingetragen werden.
Daraus ergibt sich allerdings auch, dass unser Server über https erreichbar sein muss!
Apache mit HTTPS
Ein selbstsigniertes Zertifikat ist für unseren PoC vollkommen ausreichend. Eine Signierung durch zB Let's Encrypt ist nicht notwendig.
Für diesen PoC brauchen wir lediglich den Reverse Proxy, der im ersten Schritt eine Seite über http und https ausliefern kann. Später kommt dann die Authentifizierung mit OneLogin hinzu.
Schritt 1: http
Das Dockerfile des Apache aus den vorherigen PoCs uA schon mit OpenID Module:
FROM httpd:2.4
RUN apt update && apt install -y \
libapache2-mod-auth-openidc \
ca-certificates
RUN cp /usr/lib/apache2/modules/mod_auth_openidc.so /usr/local/apache2/modules/
RUN mv conf/httpd.conf conf/container_httpd.conf
CMD ["httpd-foreground"]
Die Einbindung der Konfigurationsdateien erfolgt über reverseproxy_httpd.conf, die in den Container als httpd.conf hineinkopiert wird:
# load original configuration first
Include conf/container_httpd.conf
# customized configuration
ServerName reverseproxy
Include conf/reverseproxy.conf
Include conf/reverseproxy-ssl.conf
Die Konfiguration für http erfolgt in reverseproxy.conf:
Die Seite ist über http://localhost:443 erreichbar. Richtig: http, nicht https! Verschlüsselt wird hier noch nix.
Schritt 3: https
Als ersten Teilschritt wird die reverseproxy-ssl.conf mit der Default-SSL-Konfiguration überschrieben und alle Kommentare gelöscht.
Folgende Änderungen werden gemacht:
ServerName localhost:443
ServerAdmin deringo@github.com
SSLEngine off
#SSLCertificateFile "/usr/local/apache2/conf/server.crt"
#SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"
Server neu starten, anschließend ist die Seite weiterhin unverschlüsselt über http://localhost:443 erreichbar.
Als nächster Teilschritt wird SSLEngine on gestellt und die beiden CertificateFile Einträge wieder ent-kommentiert. Außerdem müssen die Module mod_socache_shmcb.so und mod_ssl.so geladen werden.
Server neu durchstarten und der unverschlüsselte Aufruf wird abgewiesen:
Der verschlüsselte Aufruf über https://localhost funktioniert:
Allerdings wird die Seite als "Nicht sicher" angezeigt und es musste einmalig ein entsprechender Hinweis weggeklickt werden, da das Zertifikat nicht von einer vertrauenswürdigen Stelle signiert wurde.
Da am Ende praktisch nichts von der Standard-Konfiguration für SSL geändert wurde, wird die Konfiguration wieder vereinfacht:
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf
Schritt 4: OneLogin
Die OneLogin Konfiguration wird hinzugefügt:
## Default SSL
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
Include conf/extra/httpd-ssl.conf
##
LoadModule proxy_module modules/mod_proxy.so
LoadModule xml2enc_module modules/mod_xml2enc.so
LoadModule proxy_html_module modules/mod_proxy_html.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule auth_openidc_module modules/mod_auth_openidc.so
<VirtualHost localhost:443>
ServerAdmin deringo@github.com
DocumentRoot "/usr/local/apache2/htdocs"
ServerName localhost:443
## mod_auth_openidc
## https://github.com/zmartzone/mod_auth_openidc
#this is required by mod_auth_openidc
OIDCCryptoPassphrase <INSERT-HERE a-random-secret>
OIDCProviderMetadataURL <INSERT-HERE>
OIDCClientID <INSERT-HERE>
OIDCClientSecret <INSERT-HERE>
# OIDCRedirectURI is a vanity URL that must point to a path protected by this module but must NOT point to any content
OIDCRedirectURI https://localhost/private/redirect_uri
## OIDCScope params
## to put params including roles into header
OIDCScope "openid email profile groups params"
<Location /private/>
AuthType openid-connect
Require valid-user
</Location>
</VirtualHost>
Alle Seiten sind über https erreichbar.
Aber leider auch die Private Page OHNE dass vorher ein Login über OneLogin erfolgen musste.
Irgendwie klappt das nicht mit dem überschreiben/erweitern der default SSL Konfiguration. Also doch alles in eine Datei:
LoadModule proxy_module modules/mod_proxy.so
LoadModule xml2enc_module modules/mod_xml2enc.so
LoadModule proxy_html_module modules/mod_proxy_html.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule auth_openidc_module modules/mod_auth_openidc.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
Listen 443
SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES
SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES
SSLHonorCipherOrder on
SSLProtocol all -SSLv3
SSLProxyProtocol all -SSLv3
SSLPassPhraseDialog builtin
SSLSessionCache "shmcb:/usr/local/apache2/logs/ssl_scache(512000)"
SSLSessionCacheTimeout 300
##
## SSL Virtual Host Context
##
<VirtualHost _default_:443>
# General setup for the virtual host
ServerName localhost:443
ServerAdmin deringo@github.com
DocumentRoot "/usr/local/apache2/htdocs"
ErrorLog /proc/self/fd/2
TransferLog /proc/self/fd/1
# SSL Engine Switch:
# Enable/Disable SSL for this virtual host.
SSLEngine on
SSLCertificateFile "/usr/local/apache2/conf/server.crt"
SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/usr/local/apache2/cgi-bin">
SSLOptions +StdEnvVars
</Directory>
BrowserMatch "MSIE [2-5]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
CustomLog /proc/self/fd/1 \
"%%t %%h %%{SSL_PROTOCOL}x %%{SSL_CIPHER}x \"%%r\" %%b"
## mod_auth_openidc
## https://github.com/zmartzone/mod_auth_openidc
#this is required by mod_auth_openidc
OIDCCryptoPassphrase <INSERT-HERE a-random-secret>
OIDCProviderMetadataURL <INSERT-HERE>
OIDCClientID <INSERT-HERE>
OIDCClientSecret <INSERT-HERE>
# OIDCRedirectURI is a vanity URL that must point to a path protected by this module but must NOT point to any content
OIDCRedirectURI https://localhost/private/redirect_uri
## OIDCScope params
## to put params including roles into header
OIDCScope "openid email profile groups params"
<Location /private/>
AuthType openid-connect
Require valid-user
</Location>
</VirtualHost>
So funktioniert es wie gewünscht und die Private Page wird erst nach Login angezeigt.
Allerdings nur über localhost. Nicht über die IP.
Die OIDCRedirectURI wird von https://localhost/private/redirect_uri auf https://192.168.2.149/private/redirect_uri geändert und schon funktioniert es genau anders herum: Nur über die IP, nicht über localhost.
Das ist nicht ganz so, wie ich mir das vorgestellt habe, aber für den PoC sollte es ausreichend sein.
Schritt 5: Nur https
Alles was über http rein kommt, soll über https weitergeleitet werden:
Die Generierung der Zertifikate erfolgte im laufenden Container. Dieser Schritt soll schon beim Erzeugen des Images erfolgen.
Ein Problem war, dass dies interaktiv erfolgen musste. Mit dem Parameter -subj "/" können Key & Zertifikat ohne Interaktion generiert werden. Im Subject könnten Daten eingetragen werden, dies ist aber für den PoC gar nicht erforderlich.
Einmal der Befehl vollständig, wie er im Container ausgeführt werden könnte:
Version 2 aus dem Beispiel Reverse Proxy mit OneLogin soll zu Testzwecken in der Cloud zum Laufen gebracht werden.
Bei dem PoC handelt es sich um einen Apache HTTP-Server mit simpler Public Page und OneLogin Authentifizierung für den Zugriff auf die Private Page: der ShowHeaders Anwendung in einem Apache Tomcat-Server. Beide Server laufen in Docker Containern, die über Docker Compose gestartet werden.
Cloud Server einrichten
Auf dem Server muss Docker etc. eingerichtet werden:
sudo su
yum install docker -y
systemctl umask docker
systemctl start docker
docker version
curl -L https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-`uname -s`-`uname -m` -o /usr/bin/docker-compose
chmod +x /usr/bin/docker-compose
docker-compose --version
# # Output:
# Docker Compose version v2.2.3
yum install git -y
mkdir /app
cd /app
git clone https://github.com/DerIngo/DockerOneLoginApacheSample.git
vim /app/DockerOneLoginApacheSample/version2/reverseproxy/conf/reverseproxy.conf
Fehler
Auf dem lokalen Entwicklerlaptop läuft der PoC und entsprechend soll er auch in der Cloud gestartet werden:
sudo su
cd /app/DockerOneLoginApacheSample/version2
docker-compose up
Zuerst sieht alles wie gewohnt aus, bis Maven ShowHeaders bauen soll:
root@showheaders:/usr/local/tomcat/ShowHeaders# mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< deringo:ShowHeaders >-------------------------
[INFO] Building ShowHeaders 0.0.1-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/tomcat/tomcat-catalina/8.5.53/tomcat-catalina-8.5.53.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.956 s
[INFO] Finished at: 2022-06-07T12:28:46Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project ShowHeaders: Could not resolve dependencies for project deringo:ShowHeaders:war:0.0.1-SNAPSHOT: Failed to collect dependencies at org.apache.tomcat:tomcat-catalina:jar:8.5.53: Failed to read artifact descriptor for org.apache.tomcat:tomcat-catalina:jar:8.5.53: Could not transfer artifact org.apache.tomcat:tomcat-catalina:pom:8.5.53 from/to central (https://repo.maven.apache.org/maven2): Transfer failed for https://repo.maven.apache.org/maven2/org/apache/tomcat/tomcat-catalina/8.5.53/tomcat-catalina-8.5.53.pom: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/DependencyResolutionException
Es kann eine Dependency nicht aufgelöst werden und im Text findet sich dieser Satz: "PKIX path building failed". Also irgendwas mit Zertifikaten, wie mir die Erfahrung sagt.
Nach einer Recherche ist klar: In dem Cloud Setup wird der ausgehende Traffic durch einen Proxy geleitet und dessen Zertifikate sind nicht in den Standard-Zertifikaten enthalten.
Simple Lösung: Validierung aushebeln
Die schnellste Lösung: Einfach auf jedwede Validierung verzichten.
In dem Dockerfile von ShowHeaders wird der Maven Aufruf um obige Parameter ergänzt:
WORKDIR ShowHeaders
# aus
RUN mvn package
# wird
RUN mvn package -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -Dmaven.wagon.http.ssl.ignore.validity.dates=true
und schon kann das Image wieder erfolgreich gebaut werden.
Der gleiche Fehler
Die Images werden gebaut, die Container starten und die Public Page wird korrekt angezeigt auf http://localhost:80/public
Aber: die Private Page auf http://localhost:80/private wird nicht angezeigt, bzw. wird nicht mal das OneLogin Login angezeigt.
Das Problem findet sich in der OpenID Konfiguration für OneLogin:
Das funktioniert prinzipiell, dauert aber sehr lange. Außerdem wird in einem ausführlichen Kommentar darauf hingewiesen, dass die Intermediate Certificates so nicht gezogen würden.
Eine ausführliche Anleitung findet sich auf Baeldung, deren Ansatz ist wesentlich schneller (von mir nicht auf Intermediate C. getestet):
Leider kann momentan nur auf der Konsole getestet werden:
curl localhost/private/
Aber der Docker Output sieht nicht gut aus: "SSL certificate problem: unable to get local issuer certificate":
version2-reverseproxy-1 | [Tue Jun 07 13:48:25.115383 2022] [auth_openidc:error] [pid 9:tid 140428228966144] [client 172.21.0.1:57644] oidc_util_http_call: curl_easy_perform() failed on: https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration (SSL certificate problem: unable to get local issuer certificate)
version2-reverseproxy-1 | [Tue Jun 07 13:48:25.115513 2022] [auth_openidc:error] [pid 9:tid 140428228966144] [client 172.21.0.1:57644] oidc_provider_static_config: could not retrieve metadata from url: https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configurationversion2-reverseproxy-1 | 172.21.0.1 - - [07/Jun/2022:13:48:25 +0000] "GET /private/ HTTP/1.1" 500 531
version2-reverseproxy-1 | 172.21.0.1 - - [07/Jun/2022:13:48:27 +0000] "GET /private/ HTTP/1.1" 302 478
Allerdings, nach einem Neustart sieht es doch gut aus in der Konsole:
curl localhost/private/
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1><p>The document has moved <a href="https://deringo-dev.onelogin.com/oidc/2/auth?response_type=code&scope=openid&client_id=781a5c80-a77f-013a-c94a-020f214c554637671&state=4RRlolG4XrN45Q3yCOc15Hf_6Ns&redirect_uri=http%%3A%%2F%%2Flocalhost%%2Fprivate%%2Fredirect_uri&nonce=B-PSe0ZSm6ZIoHebvpVtNQnv_JHqQLeSMpkI5tuoEi0">here</a>.</p>
</body></html>
Anschließend wieder gestoppt, Reverse Proxy Image neu bauen lassen, Container wieder gestartet: Beim ersten curl kommt der "SSL certificate problem: unable to get local issuer certificate"-Fehler, bei weiteren curls der "The document has moved"-Hinweis.
Schauen wir mal, wie das im Container aussieht:
Vorbereitung:
# frisch starten:
docker-compose down -v
docker-compose build reverseproxy
docker-compose up
# vor dem ersten Aufruf: Ab in den Container:
docker exec -it version2-reverseproxy-1 bash
Im Container:
# Im Container:
apt install curl
# 1. Aufruf
curl https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
# 2. Aufruf
curl https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
# 3. Aufruf
curl https://deringo-dev.onelogin.com/oidc/2/.well-known/openid-configuration
{"acr_values_supported":["onelogin:nist:level:1:re-auth"],"authorization_endpoint":"https://deringo-dev.onelogin.com/oidc/2/auth","claims_parameter_suppor[...]
Ich habe es noch ein paar mal ausprobiert, ungefähr jedes zweite bis dritte Mal hat es funktioniert.
Offensichtlich ist da noch ein LoadBalancer zwischen. 🤦♂️🤦♂️🤦♂️
Mehrmaliges pingen gab zwei verschiedene IPs zurück.
apt install nmap
nmap deringo-dev.onelogin.com
Starting Nmap 7.80 ( https://nmap.org ) at 2022-06-07 14:18 UTC
Nmap scan report for deringo-dev.onelogin.com (52.29.255.229)
Host is up (0.0052s latency).
Other addresses for deringo-dev.onelogin.com (not scanned): 52.29.255.230
rDNS record for 52.29.255.229: ec2-52-29-255-229.eu-central-1.compute.amazonaws.com
Not shown: 994 filtered ports
PORT STATE SERVICE
21/tcp open ftp
53/tcp open domain
80/tcp open http
443/tcp open https
554/tcp open rtsp
1723/tcp open pptp
Aha! Es gibt zwei IPs zu dem Domainnamen und anscheinend ist OneLogin in den AWS.
Nochmal ein anderer Test; Wie ist das denn, wenn ich nicht im Container, sondern direkt vom Host curle:
Jeder Aufruf funktioniert, diesmal sieht es wirklich gut aus.
Umsetzung
Das Dockerfile des Reverse Proxies:
FROM httpd:2.4
RUN apt update && apt install -y \
libapache2-mod-auth-openidc \
ca-certificates
COPY /etc/pki/ca-trust/source/anchors/ZscalerCloudChain.crt /usr/local/share/ca-certificates/ZscalerCloudChain.crt
RUN update-ca-certificates
RUN cp /usr/lib/apache2/modules/mod_auth_openidc.so /usr/local/apache2/modules/
RUN mv conf/httpd.conf conf/container_httpd.conf
CMD ["httpd-foreground"]
Anschließend das Image neu bauen:
docker-compose build reverseproxy
Ich fasse es nicht:
Step 3/6 : COPY /etc/pki/ca-trust/source/anchors/ZscalerCloudChain.crt /usr/local/share/ca-certificates/ZscalerCloudChain.crt
1 error occurred:
* Status: COPY failed: file not found in build context or excluded by .dockerignore: stat etc/pki/ca-trust/source/anchors/ZscalerCloudChain.crt: filedoes not exist, Code: 1
FROM httpd:2.4
RUN apt update && apt install -y \
libapache2-mod-auth-openidc \
ca-certificates
COPY ./ZscalerCloudChain.crt /usr/local/share/ca-certificates/ZscalerCloudChain.crt
RUN update-ca-certificates
RUN cp /usr/lib/apache2/modules/mod_auth_openidc.so /usr/local/apache2/modules/
RUN mv conf/httpd.conf conf/container_httpd.conf
CMD ["httpd-foreground"]
cp /etc/pki/ca-trust/source/anchors/ZscalerCloudChain.crt /app/DockerOneLoginApacheSample/version2/reverseproxy/ZscalerCloudChain.crt
docker-compose build reverseproxy
docker-compose up
Test
curl localhost/private/
Keine Fehler im Log! 🎉
Test im Browser mit Login
Steht noch aus, aktuell ist der Server nur über Console zu erreichen.
UPDATE: Inzwischen wurde der Zugriff per Browser auf den Server freigeschaltet (bzw. eine fehlerhafte Netzwerkkonfiguration gefixt) und die Public Seiten sind zugänglich.
Bei Aufruf der Private Seiten kommt allerdings eine Fehlermeldung:
Wir wollen unsere Anwendung durch einen Filter schützen, der nur Anfragen von eingeloggten Benutzern mit der richtigen Rolle hindurch lässt.
Für diesen PoC bauen wir einen Reverse Proxy für den Login und einen Anwendungsserver für den Filter. Der Anwendungsserver bekommt einen Zugang über den RP und einen Zugang ohne RP, um so zu zeigen, dass nur über den RP auf die Anwendung zugegriffen werden kann.
In produktiven Umgebungen darf es den Zugang ohne RP so ungeschützt nicht geben, da es sehr einfach ist, zB über ein Browser-Plugin, den RP mittels selbstgesetzter Header zu faken.
Reverse Proxy
Der Reverse Proxy bekommt grundsätzlich die gleiche Konfiguration wie in den Beispielen zuvor.
Hier müssen wir aber noch den Scope params hinzufügen, um so die OneLogin-Rollen des Benutzers zu übertragen:
## OIDCScope params
## to put params including roles into header
OIDCScope "openid email profile groups params"
Im nächsten Fenster des Dialoges die Value ("User Roles") setzen:
Access Filter
Der Access Filter prüft, ob eine UserID vorhanden ist. Das ist in unserem Beispiel die Email, es könnte aber auch der Preferred Username genommen werden.
Des weiteren prüft der Access Filter, ob der Benutzer die erforderliche Rolle user hat.
Sind beide Bedingungen erfüllt, kann auf die Anwendung zugegriffen werden, ansonsten wird lediglich "Access denied" angezeigt.
Im Docker File des Anwendungsservers kopieren wir erst nur das Maven POM, lassen dann Maven bauen, um so die Abhängigkeiten herunterzuladen. Erst danach kopieren wir das gesamte Projekt in den Container und lassen die Anwendung bauen. So wird nach Code Änderungen, ohne Anpassungen in Maven, der Bau des Images beschleunigt, da nicht jedes Mal die Bibliotheken heruntergeladen werden müssen.
FROM tomcat:8.5-jdk8-openjdk-slim
RUN apt update && apt install -y \
maven
COPY pom.xml /app/pom.xml
WORKDIR /app
RUN mvn package
COPY . /app
RUN mvn package
WORKDIR $CATALINA_HOME
RUN mv /app/target/ROOT.war webapps
EXPOSE 8080
CMD ["catalina.sh", "run"]
Test
Zugriff auf den Anwendungsserver über den Reverse Proxy nach Anmeldung:
http://localhost/private/test.html
Direkter Zugriff auf den Anwendungsserver ohne Anmeldung:
Im vorherigen Post haben wir gesehen, dass ein neuer Benutzer sich selbst registrieren kann, aber um den Zugang zur Anwendung zu erhalten, er noch durch einen User-Admin freigeschaltet/der Anwendung hinzugefügt werden muss.
Das Hinzufügen des Benutzers erfolgt in OneLogin, so dass der User-Admin auch in diesem Tool geschult werden müsste.
Um den Schulungsaufwand zu vermeiden und ein flüssigeres Arbeiten, ohne Wechsel der Anwendungen, zu ermöglichen, wollen wir einige Funktionalität von OneLogin in unserer Anwendung ermöglichen. Dazu gibt es in OneLogin eine API-Schnittstelle, die wir mit einem Java-Client ansteuern werden.
Der Java-Client logt sich einmalig mit Client ID und Secret (API Credentials! siehe unten) ein und erhält so ein Access Token, mit dem die weiteren API-Funktionen ausgeführt werden können.
Die API-Dokumentation, für Version 2, findet sich hier: https://developers.onelogin.com/api-docs/2/getting-started/dev-overview
Vorbereitung / Daten sammeln
API-Subdomain
Die API-Domain des Testprojekt ist: deringo-dev.onelogin.com
Die API-Sub-Domain lautet foglich: deringo-dev
API ID & Secret
Für die Benutzung der API wird Client ID und Client Secret benötigt. Das kennen wir schon aus einem vorherigen Post zu OneLogin:
Für den Zugriff über die API benötigen wir aber ein anderes Client ID & Secret Paar! Dieses findet sich in dem Developers-Tab unter API Credentials.
Dort ist ein entsprechender Zugang einzurichten, für das Test-Projekt haben wir mit Manage All eingerichtet:
OneLogin App ID
Die ID unserer Test-Anwendung findet sich in der URL nachdem man auf Applications -> Applications die App auswählt, in unserem Beispiel "1739301":
Java Programm aufsetzen
Abhängigkeiten
Das Projekt wird für Java 8 aufgesetzt und benötigt Apache HttpClient 4.5 und Json:
Zum Testen der API-Funktionen wird eine Test-Klasse mit den oben gesammelten Informationen angelegt und dann jeweils eine eigene Methode in der Klasse verwendet.
public class TestMain {
private static final String ONELOGIN_SUBDOMAIN = "deringo-dev";
// OneLogin Administration -> Applications -> select Application, get ID from URL
private static final String ONELOGIN_APP_ID = "1739301";
// OneLogin Administration -> Developers -> API Credentials
private static final String ONELOGIN_CLIENT_ID = "12345<geheim>67890";
private static final String ONELOGIN_CLIENT_SECRET = "12345<geheim>67890";
public static void main(String[] args) {
TestMain main = new TestMain();
main.sayHello();
}
private void sayHello() {
System.out.println("Hello World!");
}
}
Wenn zB nur die Rollen für unsere Test-App angezeigt werden sollen und der Rollen Name ein "u" enthalten muss, setzt sich der resourceURL-String wie folgt zusammen:
Wollen wir uns anstelle der gesamten User-Information lediglich Vor- und Nachname zurückgeben lassen, neben der ID, dann brauchen wir lediglich den Query Parameter anpassen:
Seitenweises Vor- oder Zurückblättern ist möglich, dazu gibt es den Parameter "cursor". Im Header der ersten Paginierten Response gibt es, falls vorhanden, After-Curser und Before-Curser. Über diese Werte kann vor- und zurückgeblättert werden.
Beispielsweise um die erste Seite zu erhalten und anschließend eine Seite weiter blättern:
Es soll zuerst ein neuer User angelegt werden. Anschließend bearbeitet, der Anwendung hinzugefügt und wieder entfernt und abschließend gelöscht werden.
HTTP/1.1 201 Created
{"firstname":null,"updated_at":"2022-06-01T14:34:52.423Z","role_ids":[],"invitation_sent_at":null,"member_of":null,"distinguished_name":null,"email":null,"id":178924216,"password_changed_at":null,"manager_ad_id":null,"group_id":null,"invalid_login_attempts":0,"phone":null,"title":null,"preferred_locale_code":null,"department":null,"samaccountname":null,"lastname":null,"custom_attributes":{"my_role":null},"created_at":"2022-06-01T14:34:52.423Z","directory_id":null,"locked_until":null,"status":7,"company":null,"activated_at":null,"last_login":null,"trusted_idp_id":null,"manager_user_id":null,"username":"min.requirements","comment":null,"external_id":null,"state":1,"userprincipalname":null}
new User ID: 178924216
Nächstes Beispiel mit Vor-, Nach- und Usernamen, Passwort und schönerem JSON:
HTTP/1.1 201 Created
{"firstname":"Happy","updated_at":"2022-06-01T14:43:30.646Z","role_ids":[],"invitation_sent_at":null,"member_of":null,"distinguished_name":null,"email":null,"id":178924824,"password_changed_at":"2022-06-01T14:43:30.629Z","manager_ad_id":null,"group_id":null,"invalid_login_attempts":0,"phone":null,"title":null,"preferred_locale_code":null,"department":null,"samaccountname":null,"lastname":"Gilmore","custom_attributes":{"my_role":null},"created_at":"2022-06-01T14:43:30.646Z","directory_id":null,"locked_until":null,"status":1,"company":null,"activated_at":"2022-06-01T14:43:30.644Z","last_login":null,"trusted_idp_id":null,"manager_user_id":null,"username":"happy.gilmore","comment":null,"external_id":null,"state":1,"userprincipalname":null}
new User ID: 178924824
Unser neuer User Happy Gilmore ist angelegt und kann sich mit seinem Username & Passwort auch anmelden, kommt aber, wie erwartet, noch nicht auf die Anwendung, da er ihr noch nicht zugewiesen wurde:
HTTP/1.1 200 OK
{"firstname":"Very Happy","state":1,"manager_user_id":null,"department":null,"comment":null,"created_at":"2022-06-01T14:43:30.646Z","email":null,"last_login":"2022-06-01T14:46:10.795Z","lastname":"Gilmore","activated_at":"2022-06-01T14:43:30.644Z","directory_id":null,"updated_at":"2022-06-01T14:57:22.656Z","group_id":null,"samaccountname":null,"status":1,"userprincipalname":null,"trusted_idp_id":null,"title":null,"manager_ad_id":null,"invalid_login_attempts":0,"locked_until":null,"phone":null,"role_ids":[],"invitation_sent_at":null,"company":null,"distinguished_name":null,"external_id":null,"password_changed_at":"2022-06-01T14:43:30.629Z","preferred_locale_code":null,"id":178924824,"member_of":null,"username":"happy.gilmore","custom_attributes":{"my_role":null}}
User ID: 178924824
Firstname: Very Happy
User einer Anwendung hinzufügen
Als nächstes sollte der User einer Anwendung hinzugefügt werden, um so den Zugang zu gewähren.
Überraschenderweise gibt es aber keine API, um einer App einen User hinzuzufügen. irgendwie ist das nicht ganz nachzuvollziehen, da dies über die OneLogin-Webseite möglich ist und es einen API Call gibt, um alle User einer App anzeigen zu lassen (List App Users - OneLogin API).
Ein Erklärungs- und somit Lösungsansatz lautet:
The proper way to do this is to assign applications to a Role in the admin console and then use the APIs to set the roles for the user.
This, in turn grants the user access to the application assigned to the role.
Die Überprüfung in der OneLogin Webseite ergibt, dass der User in der Tat hinzugefügt wurde, und es wurden auch keine vorhandenen User aus der Rolle entfernt, die nicht in dem Array enthalten waren.
Unser neuer User Happy Gilmore ist angelegt und kann sich mit seinem Username & Passwort auch anmelden, kommt jetzt auf die Anwendung, da er ihr über die Rolle "user" zugewiesen wurde:
Die Registrierung ist erreichbar unter: https://deringo-dev.onelogin.com/self-registration2?name=http://localhost
Die Email:
Nach klicken auf den Link erscheint folgender Text:
Es wird auf einen Login-Screen weitergeleitet. Allerdings wurde noch kein Passwort vergeben.
Währenddessen wurde die Mail zum Setzen des Passworts geschickt:
Über den Link wird das Passwort gesetzt und anschließend kann man sich einloggen in OneLogin:
Zugang in die Anwendung - 1. Versuch
Der Zugang zur Anwendung wird verwehrt:
Die Benutzerin Erika Mustermann konnte authentifiziert werden, ist aber für die Anwendung nicht authorisiert.
Benutzer Zugang zur Anwendung gewähren
Übersicht der Users:
Erika Mustermann auswählen und auf Applications gehen:
Dort wird sie der Applikation hinzugefügt.
Zugang in die Anwendung - 2. Versuch
Der Zugang zur Anwendung wird jetzt gewährt:
Admin benachrichtigen
Um den Workflow rund zu machen, benötigen wir noch eine Benachrichtigung an die User-Admins, sobald sich ein neuer Benutzer registriert hat.
Den Reiter Activity -> Notifications auswählen.
Eine neue Notification anlegen.
In Conditions als Event Self registration created (203) auswählen. Es gibt mehrere Events mit Self registration, da mir nicht klar ist, wann genau welcher Event ausgelöst wird, habe ich den 203 gewählt, der klingt so, als ob er getriggert wird, sobald die SR vollendet ist.
Als Action Email user mit Custom Field, so können wir die Email-Adresse selbst bestimmen, zB für einen Support-Postkorb.
Oben kann man der Notification Regel auch noch einen Titel vergeben, dort, wo im Screenshot Untitled steht
Nach dem Speichern der Notification hat ein neuer Benutzer Happy Gilmore den Registrierungsprozess durchlaufen. Dabei wurde aber keine Email verschickt.
Über Activity -> Events kann man die Events sehen, die dabei ausgelöst wurden:
In der Notification wird der Event auf Self registration approved (101) geändert, dann der neue Benutzer gelöscht, so dass er sich wieder neu registrieren kann.
Nachdem der Benutzer sich erneut registriert hat, wird diesmal eine Email verschickt:
Die Mail enthält den Text (Subject & Body), der vorher in der Notification hinterlegt wurde. Leider gibt es keinen Hinweis, welcher User sich registriert hat.
Ein Blick in die bereits vordefinierten Notifications zeigt, dass es Variablen gibt und diese in doppelten geschweiften Klammern gesetzt sind.