‏ ‏ ‎ ‏ ‏ ‎

1. 2024-09-13 http-Filter

  • Ein Filter kann den Request überprüfen bzw den Response ändern

  • in der Cloud können wir keine Cookies verwenden, da diese am Client liegen. Der Server muss immer in der DB nachschauen (oder der Client müsste immer zum selben Server groutet werden) → das wäre zu langsam.

    • Sticky Session sind nicht optimal, da der LoadBalancer den Client immer zum selben Pod schicken würde.

    • Lösungen wie Keycloak verwenden trotzdem Cookies (ist performancemäßig egal, da man sich eh nicht so oft authentifiziert → ebenfalls DB-Zugriff)

2. 2024-09-20

2.1. container request filter

  • Prüft, ob im Header die Authentifizierung (wer bin ich) passt.

…​

Problem:

  • Die zwei Filter sind unabhängig

  • Wir müssen jedoch username und password vom Authentication Filter zum Authorization Filter übergeben

  • Dazu verwenden wir den ContainerRequestContext

  • Labs-Ordner im GitHub

HÜ:

  • Unit-Tests erstellen mit rest-assured

  • beim Login Session in DB erstellen (Cookies in DB speichern)

  • wir verwenden als DB H2

3. 2024-09-27

3.1. Betriebsmodi für Datenbanken

betriebsmodi datenbanken

4. Aufgabenstellung

  • ein User-Tabelle wird erstellt (Password hashen und salten) und auch Session-Tabelle

  • eigene Annotation @AllowAll wird erstellt

  • Ablauf

    • nach Überprüfen des Usernames und Password wird eine Session in die DB eingetragen mit FK auf User

    • die Session erhält eine UUID und einen Zeitstempel (Gültigkeit zB 1h)

    • die Session wird nun mit Set-Cookie an den Browser gesendet

    • die Browser senden nun dieses Cookie bei jedem Request an den Endpoint zurück

    • im Authentication-Filter (wer bin ich) wird aus dem Cookie der User durch DB-Zugriff festgestellt.

    • Was haben wir bisher:

      • Wir haben einen Login-Request mit AllowAll

      • Alles wird durchgelassen, es wird in der Datenbank eine Session erstellt.

      • Als Response des Login-Requests wird ein Cookie dem Browser übergeben

      • Bei jeden (hello-) Request wird ein Cookie mitgesendet.

    • Was ist zu tun?

      • Bei jedem neuen Request wird aus der Session Tabelle der User herausgeholt (Wer bin ich - Filter)

      • Diese User-Credentials werden in den Context-Properties gespeichert.

      • Im Was darf ich - Filter wird für jeden Endpoint kontrolliert, ob eine User Id vorhanden ist

    • Kommende Woche

      • @RolesAllowed

      • Rollenkonzept einführen

5. @Provider in JAX-RS

6. @Produces von CDI

  • https://weld.cdi-spec.org/

  • Eine Objekt wird beim @Inject nicht vom Container instanziert, sondern meine Funktion mit "new" wird verwendet

7. 2024-10-04

input throughput output
schnittstellen kontextdiagramm
schnittstellen kontextdiagramm

7.1. JWT

  • Bislang haben wir Cookies verwendet.

  • Nachteil: Bei jedem Request erfolgt ein Datenbankzugriff

  • Abhilfe> Sticky Session

    • Ein User wird immer mit dem ersten Server verbunden, mit dem er Kontakt hatte (Lastverteilung)

    • Auf diesem Server werden die Cookies auf das File System (oder lokale DB) geschrieben

    • Dieses Verfahren ist daher nicht optimal

    • Abhilfe: JWT

  • Rolle: Gruppierung von Rechten

  • Usergroup: Gruppierung von Usern

loadbalancer pods db

7.1.1. Aufbau

  • Ist eine Konvention, wie eine JWT geschickt wird (Struktur und keine Zufallszahl wie bei Cookies)

  • Struktur besteht aus drei Teilen

    • header

    • payload

    • signature

  • iss in payload muss überprüft werden → Wer hat das Zertifikat ausgestellt?

  • https://jwt.io/introduction

7.2. MOB-Programming

  • alle - ausser dem Driver - schließen den Laptop

  • Navigator: hört sich an, was der Mob ansagt und filtert das richtige raus und diktiert dem Driver

  • Driver: tippt den Code

  • nach ca. 15 min wird gewechselt

7.3. git-secret

❯ gpg --list-keys
[keyboxd]
---------
pub   ed25519 2024-03-15 [SC]
      14705EAD108F834E310178E5191650E41055DC8E
uid           [ultimate] Thomas W. Stütz <t.stuetz@htl-leonding.ac.at>
sub   cv25519 2024-03-15 [E]

❯ gpg --armor --export t.stuetz@htl-leonding.ac.at
-----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEZfQUoxYJKwYBBAHaRw8BAQdAZlX/fAe4TuqQeJbl1lBcM8ZxBVR10SZSiJoe
/yPaBim0LlRob21hcyBXLiBTdMO8dHogPHQuc3R1ZXR6QGh0bC1sZW9uZGluZy5h
Yy5hdD6IkwQTFgoAOxYhBBRwXq0Qj4NOMQF45RkWUOQQVdyOBQJl9BSjAhsDBQsJ
CAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEBkWUOQQVdyOLYMBAPIZgbyPEcnI
03mKahdjEAi+UF8FPPB9ECMMoOHyCXJpAQD4TwenokZmZDuh75NATqLVOKLyG0yV
0hdHefQCx0oRDLg4BGX0FKMSCisGAQQBl1UBBQEBB0DJuoCXAZ3pytv+xKhN4yHm
JoicXGuDwa8SHc1x7uOAMgMBCAeIeAQYFgoAIBYhBBRwXq0Qj4NOMQF45RkWUOQQ
VdyOBQJl9BSjAhsMAAoJEBkWUOQQVdyOKXgBANHbZH3n/3UxyLXulvWk95jS7Yc6
JH6odqFX9xlwIteYAQCpy+sU3bh2kdbjMb6Q1Td4F1zoSsav+lB+ZPENjRlnDw==
=wAG
-----END PGP PUBLIC KEY BLOCK-----
gpg --import public_key.txt

8. 2024-11-08

8.2. Security

8.2.1. Bsp 1: Basic Authentication mit User und Password base64 codiert

authorization and authentication

8.2.2. Bsp 2: Authorization mit Annotations

  • Problem: Man braucht public-Ressourcen um einen Anlaufpunkt fürs Anmelden zu haben bzw als Frontpage

  • Wir möchten mit Annotations arbeiten, um bei Ressourcen zB @AllowAll darüberschreiben zu können

  • Es werden Annotations eingeführt

  • Erstellen einer Annotation @AllowAll

package at.htl.auth;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AllowAll {
}
  • Im authenticationFilter wird überprüft, ob AllowAll. Sicherlich besser in AuthorizationFilter

8.2.3. Bsp 3: Login in Session speichern

* Übung ** Richtigstellen: Überprüfung von AllowAll vom AuthenticationFilter zu AuthorizationFilter. Wenn bei Klasse eine strengere Permission steht und bei MEthoden eine @AllowAll, dann gilt immer die freizügigere Permission

  1. In LoginResource wird eine Session erstellt (SetCookie)

GET http://localhost:8080/hello
result
Hello RESTEasy x
  • Problem: Jeder darf auf Resource zugreifen

###

POST http://localhost:8080/login
Content-Type: application/json

{
  "username": "chris",
  "password": "password"
}

> {% client.global.set("cookie", response.headers.valueOf("Set-Cookie")); %}
###

GET http://localhost:8080/hello
Cookie: {{cookie}}
###
result
2024-11-08 11:30:19,447 INFO  [at.htl.aut.AuthenticationFilter] (executor-thread-1) Session: Session=b3d07c5b-67e8-4813-8591-0dd2efda6dd5
  • Rollen werden erstellt

Nie in die Lebensdauer von JPA-Entities eingreifen

8.3. Exkurs: container managed persistance vs manuelle Transaktionen

zB Gehälter

Transaktion-Begin
...
Transaktions-Commit
  • wenn Problem mittendrin, dann sind die ersten Gehälter bereits überwiesen, die späteren sind noch offen

  • daher SET_ROLLBACK_ONLY verwenden

    • Sämtliche Teiltransaktionen werden zurückgerollt

8.4. Norman Doors

  • Erwartungskonformität

9. 2024-12-13

kanonische applikation

10. 2025-02-28 KEycloak

cd compose/keycloak
docker build .    # wird nicht getagged

docker compose --build

docker container ls -a
docker container ls -aq | xargs docker container rm
docker container la -a
  • Empfehlung: Den Realm einmal exportieren und beim Hochfahren automatisch importieren

export-realm.sh
#!/usr/bin/env bash


#/opt/keycloak/bin/kc.sh export --db postgres --db-url-host postgres --db-url-database jdbc:postgresql://postgres/keycloak --db-username keycloak --db-password keycloak --realm demo --dir /opt/keycloak/export

/opt/keycloak/bin/kc.sh export --db postgres --db-url jdbc:postgresql://postgres/keycloak \
    --db-username keycloak --db-password keycloak \
    --realm demo --dir /opt/keycloak/export --users same_file \
    --features-disabled=admin-api,admin2
kontrollieren ob export-realm.sh inkludiert ist
docker compose exec -it keycloak bash
docker compose cp ./keycloak/export-realm.sh keycloak:/usr/local/bin
docker compose exec -it keycloak bash
chmod +x /usr/local/bin/export-realm.sh
Dockerfile
#FROM --platform=linux/amd64 quay.io/keycloak/keycloak:26.0 AS builder
FROM quay.io/keycloak/keycloak:26.0 AS builder

# Enable health and metrics support
ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true

# Configure a database vendor
ENV KC_DB=postgres

WORKDIR /opt/keycloak

# for demonstration purposes only, please make sure to use proper certificates in production instead
RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 \
    -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore
RUN /opt/keycloak/bin/kc.sh build --health-enabled=true --metrics-enabled=true

#FROM --platform=linux/amd64 quay.io/keycloak/keycloak:26.0
FROM quay.io/keycloak/keycloak:26.0
COPY --from=builder /opt/keycloak/ /opt/keycloak/
COPY ./import/ /opt/keycloak/data/import/
USER root
COPY ./export-realm.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/export-realm.sh
USER keycloak
# change these values to point to a running postgres instance
ENV KC_DB=postgres
ENV KC_DB_URL=keycloak
ENV KC_DB_USERNAME=keycloak
ENV KC_DB_PASSWORD=keycloak
ENV KC_HOSTNAME=localhost
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
backup erstellen
docker compose exec export-realm.sh
  • 1. Varinate zum Sichern

    • Sichern über shell skript

    • Beim Hochfahren mit import-Option restoren.

  • 2. Variante zum Sichern

    • pgdump der Datenbank

    • Da sind dann auch die Passwörter dabei

Sicherung ohne cron-job (backup-keycloak.sh)
#!/usr/bin/env bash

# to use this script backup container must be running, so start the following before:
# docker compose --file backup-keycloak.yaml up

set -e
mkdir -p ./target
docker compose exec postgres pg_dump --dbname=keycloak --username=keycloak | gzip > ./target/keycloak.sql.gz
#docker compose cp backup:/export ./target

#cp ./target/export/*.json ./keycloak/import
  • keycloak kann auch write-ahead logs (redo-logs in Oracle)

10.1. Keycloak Web-Console

  • Client scopes: Welche Rechte in welchem Kontext (Scope)

    • Read-Scopes: nur GETs, keine POST und PUTs erlaubt

    • Edit-Scope: für POST und PUTs

  • Roles: Sammlung von Rechten

  • Groups: Sammlung von Usern

10.1.1. Clients

  • Frontend

  • Varianten

    • Standard-Flow: Browser → Keycloak Plugin kontrolliert JWT ob die benötigte Rolle enthalten ist → wenn nicht (403) wird an den Keycloak weitergeleitet zur Authentifizierung

der Keycloak fragt das Backend, ob es erlaubt ist (im Backend gibt es dafür Annotationen zB @RolesAllowed) und gibt ein Token an den Browser zurück

  • Authorization: Der Request wird von einem Authentication & Authorization-Filter (keycloak-plugin) geprüft, ob der User die Rechte hat. Es erfolgt dabei kein Zugriff auf das Backend, da sämtliche Informationen (Policy) im Keycloak enthalten sind.

10.1.2. Groups & Roles

  • Die Rollen sind den Usern nicht direkt zugeordnet, sondern über Gruppen

10.1.3. Secret

  • für Verwendung von Secrets Client authentication einschalten

  • Direct Access Grant: Man kann direkt mit Username und Password mit dem Keycloak "sprechen".