fe429dbe01
- Links anlegen und aendern: Passwort, Ablaufdatum und Bezeichnung nur im JSON-Koerper, Vorpruefung aus den frisch gelesenen Faehigkeiten (Passwort/Ablauf verlangt, Hochladen, mehrere Links), echte Datumspruefung, nur geaenderte Felder beim Aendern - Routen shares/mine, shares/received (angenommen plus offen, 404/405 bei offen = leer) und POST shares/:id/accept am Ende der Klasse - Fehlerzweige fuer Passwort und Ablauf mit Tests; nie Passwort in Antwort oder Fehler - Live-Test e2e-shares.sh: Abschnitte links (Pflicht-Passwort und Pflicht-Ablauf per occ) und received (eingehend, offen, annehmen, verlassen) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
470 lines
24 KiB
Bash
470 lines
24 KiB
Bash
#!/usr/bin/env bash
|
|
# quick-261009-dkv: Teilen-Schnittstelle gegen die Test-Nextcloud (tessera-nc-test).
|
|
# Aufruf: e2e-shares.sh [people|links|received|version|all] (Vorgabe: all)
|
|
# Aufgabe 1 liefert Aufbau, Aufraeumen und den Abschnitt "people"; Aufgabe 2 die Abschnitte
|
|
# "links" (Passwort- und Ablaufregeln der Nextcloud per occ umgeschaltet) und "received"
|
|
# (eingehende und offene Freigaben); version folgt in Aufgabe 3.
|
|
# Setzt einen laufenden Stack (api neu gebaut: docker compose up -d --build api) und
|
|
# nc-test-setup.sh voraus. Nur Testwerte; liest keine .env-Dateien.
|
|
#
|
|
# Die Test-Nextcloud bekommt den Benutzer ben (Passwort User3-Pass-12345, ohne Zwei-Faktor) und die
|
|
# Gruppe tessera-team (mit ben). Die Ratenbegrenzung der Nextcloud wird NUR fuer diesen Lauf
|
|
# ausgeschaltet (sonst koennte wiederholtes Testen deren 429 ausloesen, das die Aufrufsperre fuer
|
|
# den ganzen Ursprung anhaelt). Die Begrenzung von Tessera (15 in 10 Minuten) beweisen die
|
|
# Einheitstests. Alles, was das Skript anfasst, wird im trap zurueckgesetzt.
|
|
set -euo pipefail
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$HERE/../../261008-mzu-modul-nextcloud-dateien-eigenstaendiger-/e2e/e2e-lib.sh"
|
|
|
|
SECTION=${1:-all}
|
|
case "$SECTION" in people | links | received | version | all) ;; *) e2e_fail "unbekannter Abschnitt: $SECTION" ;; esac
|
|
|
|
FILES="$API/modules/nextcloud-files"
|
|
NC_DAV="http://localhost:18080/remote.php/dav"
|
|
NC_OCS="http://localhost:18080/ocs/v2.php/apps/files_sharing/api/v1"
|
|
ANNA="anna:User1-Pass-12345"
|
|
BEN="ben:User3-Pass-12345"
|
|
EPOCH=$(date +%s)
|
|
FIX="Tessera-Teilen-$EPOCH"
|
|
BENFIX="Ben-Ordner-$EPOCH"
|
|
BENBOX="Ben-Briefkasten-$EPOCH"
|
|
START_ISO=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
REPO="$(cd "$HERE/../../../.." && pwd)"
|
|
|
|
# --- Aufraeumen: alles zuruecksetzen, was das Skript angefasst hat ----------------------------
|
|
cleanup() {
|
|
local rc=$?
|
|
set +e
|
|
curl -s -o /dev/null -u "$ANNA" -X DELETE "$NC_DAV/files/anna/$FIX" 2>/dev/null
|
|
curl -s -o /dev/null -u "$BEN" -X DELETE "$NC_DAV/files/ben/$BENFIX" 2>/dev/null
|
|
curl -s -o /dev/null -u "$BEN" -X DELETE "$NC_DAV/files/ben/$BENBOX" 2>/dev/null
|
|
# Papierkorb leeren, damit abgelehnte oder verlassene Ordner nicht liegen bleiben
|
|
NC_OCC trashbin:cleanup ben >/dev/null 2>&1
|
|
NC_OCC config:system:delete ratelimit.protection.enabled >/dev/null 2>&1
|
|
for key in shareapi_enforce_links_password shareapi_default_expire_date \
|
|
shareapi_enforce_expire_date shareapi_expire_after_n_days; do
|
|
NC_OCC config:app:delete core "$key" >/dev/null 2>&1
|
|
done
|
|
NC_OCC user:setting --delete anna files_sharing default_accept >/dev/null 2>&1
|
|
rm -rf "$E2E_TMP"
|
|
exit $rc
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# --- Hilfen -------------------------------------------------------------------------------------
|
|
ADMIN="$E2E_TMP/admin.jar"
|
|
SEEN="$E2E_TMP/seen.txt"
|
|
: >"$SEEN"
|
|
|
|
# call <methode> <pfad-nach-/modules/nextcloud-files/> [json] — gibt den Status aus und merkt ihn in
|
|
# $SEEN fuer die Pruefung "nie 401/403" (Datei, weil $(...) in einer Unterschale laeuft). Antwortkoerper: $E2E_TMP/body.out
|
|
call() {
|
|
local code
|
|
code=$(e2e_status "$ADMIN" "$1" "$FILES/$2" "${3:-}")
|
|
echo "$code" >>"$SEEN"
|
|
echo "$code"
|
|
}
|
|
|
|
# qget <endpoint> <schluessel> <wert> [<schluessel> <wert> ...] — GET mit sauber codierter Query.
|
|
qget() {
|
|
local endpoint=$1 args=() code
|
|
shift
|
|
while [ $# -gt 0 ]; do
|
|
args+=(--data-urlencode "$1=$2")
|
|
shift 2
|
|
done
|
|
code=$(curl -s -o "$E2E_TMP/body.out" -w '%{http_code}' -b "$ADMIN" -G "${args[@]}" "$FILES/$endpoint")
|
|
echo "$code" >>"$SEEN"
|
|
echo "$code"
|
|
}
|
|
|
|
# jq_py <ausdruck> — wertet einen Python-Ausdruck auf der JSON-Datei $E2E_TMP/body.out aus (d = Daten).
|
|
jq_py() {
|
|
python3 -I -c 'import json,sys
|
|
d=json.load(open(sys.argv[1]))
|
|
print(eval(sys.argv[2]))' "$E2E_TMP/body.out" "$1"
|
|
}
|
|
|
|
# Direkt bei der Nextcloud: eingehende Freigaben von ben (JSON in $E2E_TMP/ben.out)
|
|
ben_shares() {
|
|
curl -s -u "$BEN" -H 'OCS-APIRequest: true' -H 'Accept: application/json' \
|
|
"$NC_OCS/shares?shared_with_me=true" -o "$E2E_TMP/ben.out"
|
|
}
|
|
# ben_count <ordnername> — Anzahl der eingehenden Freigaben von ben mit diesem Ziel
|
|
ben_count() {
|
|
ben_shares
|
|
python3 -I -c 'import json,sys
|
|
d=json.load(open(sys.argv[1]))["ocs"]["data"]
|
|
print(len([s for s in d if s.get("file_target","").lstrip("/")==sys.argv[2]]))' "$E2E_TMP/ben.out" "$1"
|
|
}
|
|
ben_perm() {
|
|
ben_shares
|
|
python3 -I -c 'import json,sys
|
|
d=json.load(open(sys.argv[1]))["ocs"]["data"]
|
|
print([s["permissions"] for s in d if s.get("file_target","").lstrip("/")==sys.argv[2]][0])' "$E2E_TMP/ben.out" "$1"
|
|
}
|
|
|
|
# policy_wait <python-ausdruck> <beschreibung> — wartet bis zu 30 s, bis die Richtlinie den Ausdruck
|
|
# erfuellt. Die Nextcloud haelt ihre Faehigkeiten nach einer occ-Aenderung kurz noch in einem
|
|
# Zwischenspeicher (gemessen: Sekunden), Tessera selbst speichert nichts.
|
|
policy_wait() {
|
|
local i code ok=0
|
|
for i in $(seq 1 40); do
|
|
code=$(call GET shares/policy)
|
|
if [ "$code" = "200" ] && [ "$(jq_py "$1")" = "True" ]; then
|
|
ok=$((ok + 1))
|
|
# dreimal hintereinander, falls mehrere Nextcloud-Prozesse verschieden schnell nachziehen
|
|
[ "$ok" -ge 3 ] && return 0
|
|
else
|
|
ok=0
|
|
sleep 1
|
|
fi
|
|
done
|
|
e2e_expect True "$(jq_py "$1")" "$2"
|
|
}
|
|
|
|
# Direkt bei der Nextcloud: Anzahl der Link-Freigaben (Art 3) eines Pfads in annas Konto
|
|
anna_link_count() {
|
|
curl -s -u "$ANNA" -H 'OCS-APIRequest: true' -H 'Accept: application/json' -G \
|
|
--data-urlencode "path=$1" "$NC_OCS/shares" -o "$E2E_TMP/anna.out"
|
|
python3 -I -c 'import json,sys
|
|
d=json.load(open(sys.argv[1]))["ocs"]["data"]
|
|
print(len([s for s in d if s.get("share_type")==3]))' "$E2E_TMP/anna.out"
|
|
}
|
|
|
|
# nc_put <id> <schluessel> <wert> — dieselbe Aenderung DIREKT bei der Nextcloud (nur zum Messen des
|
|
# Status, den Tessera sonst hinter einem eigenen Fehlercode verbirgt); gibt den Status aus.
|
|
nc_put() {
|
|
curl -s -o "$E2E_TMP/nc-put.out" -w '%{http_code}' -u "$ANNA" -X PUT \
|
|
-H 'OCS-APIRequest: true' -H 'Accept: application/json' \
|
|
--data-urlencode "$2=$3" "$NC_OCS/shares/$1"
|
|
}
|
|
|
|
# Direkt bei der Nextcloud: ben teilt einen Ordner mit anna (nur Ansehen); gibt den Status aus.
|
|
ben_share_with_anna() {
|
|
curl -s -o /dev/null -w '%{http_code}' -u "$BEN" -X MKCOL "$NC_DAV/files/ben/$1" >/dev/null
|
|
curl -s -o "$E2E_TMP/ben-share.out" -w '%{http_code}' -u "$BEN" -X POST \
|
|
-H 'OCS-APIRequest: true' -H 'Accept: application/json' \
|
|
--data-urlencode "path=/$1" -d shareType=0 -d shareWith=anna -d permissions=1 "$NC_OCS/shares"
|
|
}
|
|
|
|
# --- Aufbau --------------------------------------------------------------------------------------
|
|
e2e_wait_health
|
|
e2e_login "$ADMIN"
|
|
e2e_activate "$ADMIN"
|
|
e2e_set_address "$ADMIN"
|
|
code=$(e2e_connect_anna "$ADMIN")
|
|
e2e_expect 200 "$code" "anna verbinden"
|
|
|
|
code=$(call GET shares/policy)
|
|
[ "$code" = "200" ] || e2e_fail "GET shares/policy -> $code. API-Container neu bauen: docker compose up -d --build api"
|
|
|
|
# ben und die Gruppe (idempotent)
|
|
if ! NC_OCC user:info ben >/dev/null 2>&1; then
|
|
docker exec -u www-data -e OC_PASS='User3-Pass-12345' tessera-nc-test \
|
|
php occ user:add --password-from-env --display-name="Ben Beispiel" ben >/dev/null
|
|
fi
|
|
GROUPS_NOW=$(NC_OCC group:list 2>/dev/null || true)
|
|
case "$GROUPS_NOW" in *tessera-team*) ;; *) NC_OCC group:add tessera-team >/dev/null ;; esac
|
|
NC_OCC group:adduser tessera-team ben >/dev/null 2>&1 || true
|
|
NC_OCC config:system:set ratelimit.protection.enabled --value=false --type=boolean >/dev/null
|
|
|
|
# Ordner mit Datei und Unterordner in annas Konto
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' -u "$ANNA" -X MKCOL "$NC_DAV/files/anna/$FIX")
|
|
e2e_expect 201 "$code" "Fixture-Ordner"
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' -u "$ANNA" -X PUT --data-binary 'Bericht' "$NC_DAV/files/anna/$FIX/Bericht.txt")
|
|
e2e_expect 201 "$code" "Fixture Bericht.txt"
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' -u "$ANNA" -X MKCOL "$NC_DAV/files/anna/$FIX/Briefkasten")
|
|
e2e_expect 201 "$code" "Fixture Briefkasten"
|
|
|
|
# --- Abschnitt people ----------------------------------------------------------------------------
|
|
section_people() {
|
|
local code share_id group_id
|
|
|
|
code=$(call GET shares/policy)
|
|
e2e_expect 200 "$code" "Richtlinie lesen"
|
|
e2e_expect True "$(jq_py 'd["enabled"] is True and d["links"]["passwordRequired"] is False')" "Richtlinie: enabled, Passwort nicht erzwungen"
|
|
|
|
code=$(qget sharees term ben itemType folder)
|
|
e2e_expect 200 "$code" "Empfaengersuche ben"
|
|
e2e_expect True "$(jq_py 'any(s["kind"]=="user" and s["id"]=="ben" for s in d["sharees"])')" "Suche ben findet die Person"
|
|
code=$(qget sharees term tessera itemType folder)
|
|
e2e_expect 200 "$code" "Empfaengersuche tessera"
|
|
e2e_expect True "$(jq_py 'any(s["kind"]=="group" and s["id"]=="tessera-team" for s in d["sharees"])')" "Suche tessera findet die Gruppe"
|
|
code=$(qget sharees term '' itemType folder)
|
|
e2e_expect 200 "$code" "leere Suche"
|
|
e2e_expect '[]' "$(jq_py 'd["sharees"]')" "leere Suche liefert nichts"
|
|
|
|
# Personenfreigabe: Ordner an ben, Bearbeiten
|
|
code=$(call POST shares "{\"path\":\"/$FIX\",\"kind\":\"user\",\"shareWith\":\"ben\",\"access\":\"edit\"}")
|
|
e2e_expect 201 "$code" "Personenfreigabe anlegen"
|
|
e2e_expect True "$(jq_py 'd["kind"]=="user" and d["access"]=="edit" and d["permissions"]==15 and d["shareWith"]=="ben"')" "Freigabe: Person, Bearbeiten, 15"
|
|
share_id=$(jq_py 'd["id"]')
|
|
e2e_expect 1 "$(ben_count "$FIX")" "ben sieht die Freigabe"
|
|
e2e_expect 15 "$(ben_perm "$FIX")" "ben: Berechtigung 15"
|
|
|
|
# Dieselbe Freigabe nochmal: abgelehnt, kein zweiter Eintrag
|
|
code=$(call POST shares "{\"path\":\"/$FIX\",\"kind\":\"user\",\"shareWith\":\"ben\",\"access\":\"edit\"}")
|
|
e2e_expect 409 "$code" "doppelte Freigabe"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"shareAlreadyExists"' "doppelt: shareAlreadyExists"
|
|
e2e_expect 1 "$(ben_count "$FIX")" "ben sieht weiterhin genau eine Freigabe"
|
|
|
|
# Hochladen ist fuer Personen nicht moeglich, und die Berechtigung kommt nur als Auswahl
|
|
code=$(call POST shares "{\"path\":\"/$FIX/Briefkasten\",\"kind\":\"user\",\"shareWith\":\"ben\",\"access\":\"upload\"}")
|
|
e2e_expect 400 "$code" "Hochladen fuer eine Person"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"shareAccessInvalid"' "upload: shareAccessInvalid"
|
|
code=$(call POST shares "{\"path\":\"/$FIX/Briefkasten\",\"kind\":\"user\",\"shareWith\":\"ben\",\"access\":\"abc\"}")
|
|
e2e_expect 400 "$code" "unbekannte Auswahl"
|
|
|
|
# Auf Ansehen aendern
|
|
code=$(call PUT "shares/$share_id" '{"access":"view"}')
|
|
e2e_expect 200 "$code" "auf Ansehen aendern"
|
|
e2e_expect True "$(jq_py 'd["access"]=="view" and d["permissions"]==1')" "Freigabe: Ansehen, 1"
|
|
e2e_expect 1 "$(ben_perm "$FIX")" "ben: Berechtigung 1"
|
|
|
|
# Gruppenfreigabe einer Datei
|
|
code=$(call POST shares "{\"path\":\"/$FIX/Bericht.txt\",\"kind\":\"group\",\"shareWith\":\"tessera-team\",\"access\":\"view\"}")
|
|
e2e_expect 201 "$code" "Gruppenfreigabe anlegen"
|
|
e2e_expect True "$(jq_py 'd["kind"]=="group" and d["itemType"]=="file" and d["permissions"]==1')" "Gruppenfreigabe: Gruppe, Datei, 1"
|
|
group_id=$(jq_py 'd["id"]')
|
|
|
|
# Freigaben des Ordners: genau die Personenfreigabe
|
|
code=$(qget shares/by-path path "/$FIX")
|
|
e2e_expect 200 "$code" "Freigaben des Ordners"
|
|
e2e_expect True "$(jq_py 'len(d["shares"])==1 and d["shares"][0]["id"]=="'"$share_id"'"')" "by-path: genau die Personenfreigabe"
|
|
|
|
# Kennzeichen in der Dateiliste
|
|
code=$(qget files path /)
|
|
e2e_expect 200 "$code" "Wurzel auflisten"
|
|
e2e_expect True "$(jq_py 'any(e["name"]=="'"$FIX"'" and 0 in e["shareTypes"] for e in d["entries"])')" "Wurzel: Ordner traegt shareTypes mit 0"
|
|
code=$(qget files path "/$FIX")
|
|
e2e_expect 200 "$code" "Ordner auflisten"
|
|
e2e_expect True "$(jq_py 'any(e["name"]=="Bericht.txt" and 1 in e["shareTypes"] for e in d["entries"])')" "Ordner: Bericht.txt traegt shareTypes mit 1"
|
|
e2e_expect True "$(jq_py 'any(e["name"]=="Briefkasten" and e["shareTypes"]==[] for e in d["entries"])')" "Ordner: Briefkasten ist nicht geteilt"
|
|
|
|
# Entfernen
|
|
code=$(call DELETE "shares/$share_id")
|
|
e2e_expect 200 "$code" "Personenfreigabe entfernen"
|
|
e2e_expect 0 "$(ben_count "$FIX")" "ben sieht die Freigabe nicht mehr"
|
|
code=$(qget shares/by-path path "/$FIX")
|
|
e2e_expect 200 "$code" "Freigaben des Ordners nach dem Entfernen"
|
|
e2e_expect True "$(jq_py 'd["shares"]==[]')" "by-path: leer"
|
|
code=$(call DELETE "shares/$group_id")
|
|
e2e_expect 200 "$code" "Gruppenfreigabe entfernen"
|
|
code=$(call DELETE "shares/$share_id")
|
|
e2e_expect 404 "$code" "bereits entfernte Freigabe"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"shareNotFound"' "entfernt: shareNotFound"
|
|
code=$(call DELETE shares/abc)
|
|
e2e_expect 404 "$code" "ungueltige Kennung"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"shareNotFound"' "abc: shareNotFound"
|
|
|
|
echo "e2e shares people ok"
|
|
}
|
|
|
|
section_links() {
|
|
local code id1 id2 id3 id4 before pw plus3 plus30 measured ids urls=""
|
|
|
|
# Ausgangslage: keine Pflicht, kein Ablauf (Zustand des Skripts, nicht der Vorlauf)
|
|
NC_OCC config:app:delete core shareapi_enforce_links_password >/dev/null 2>&1 || true
|
|
policy_wait 'd["links"]["enabled"] is True and d["links"]["passwordRequired"] is False and d["links"]["expiryEnforced"] is False and d["links"]["expiryDefaultDays"] is None' "Ausgangslage: Links an, keine Pflicht"
|
|
|
|
# Link ohne Passwort und ohne Ablauf (freiwillig)
|
|
code=$(call POST shares "{\"path\":\"/$FIX\",\"kind\":\"link\",\"access\":\"view\",\"expireDate\":\"\"}")
|
|
e2e_expect 201 "$code" "Link anlegen"
|
|
e2e_expect True "$(jq_py 'd["kind"]=="link" and d["hasPassword"] is False and d["expiration"] is None and d["access"]=="view" and d["permissions"] & 15 == 1')" "Link: Ansehen, ohne Passwort, ohne Ablauf"
|
|
e2e_expect True "$(jq_py 'd["url"].startswith("'"$NC_BASE"'/")')" "Link-Adresse beginnt mit der Nextcloud-Adresse"
|
|
id1=$(jq_py 'd["id"]')
|
|
urls="$urls $(jq_py 'd["url"]')"
|
|
|
|
# Nur hochladen bei einem Ordner
|
|
code=$(call POST shares "{\"path\":\"/$FIX/Briefkasten\",\"kind\":\"link\",\"access\":\"upload\",\"expireDate\":\"\"}")
|
|
e2e_expect 201 "$code" "Briefkasten-Link anlegen"
|
|
e2e_expect True "$(jq_py 'd["permissions"] & 15 == 4 and d["access"]=="upload"')" "Briefkasten-Link: Berechtigung 4 (die Nextcloud setzt bei Links das Teilen-Bit dazu)"
|
|
id2=$(jq_py 'd["id"]')
|
|
urls="$urls $(jq_py 'd["url"]')"
|
|
|
|
# Nur hochladen bei einer Datei: abgelehnt, nichts bei der Nextcloud
|
|
before=$(anna_link_count "/$FIX/Bericht.txt")
|
|
code=$(call POST shares "{\"path\":\"/$FIX/Bericht.txt\",\"kind\":\"link\",\"access\":\"upload\",\"expireDate\":\"\"}")
|
|
e2e_expect 400 "$code" "Nur hochladen bei einer Datei"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"shareAccessInvalid"' "Datei-Upload: shareAccessInvalid"
|
|
e2e_expect "$before" "$(anna_link_count "/$FIX/Bericht.txt")" "Nextcloud hat keinen neuen Link fuer die Datei"
|
|
|
|
# --- Passwort wird von der Nextcloud verlangt ---
|
|
NC_OCC config:app:set core shareapi_enforce_links_password --value=true --type=boolean >/dev/null
|
|
policy_wait 'd["links"]["passwordRequired"] is True' "Richtlinie: Passwort verlangt"
|
|
|
|
before=$(anna_link_count "/$FIX")
|
|
code=$(call POST shares "{\"path\":\"/$FIX\",\"kind\":\"link\",\"access\":\"view\",\"expireDate\":\"\"}")
|
|
e2e_expect 400 "$code" "Link ohne Passwort bei Pflicht"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"sharePasswordRequired"' "ohne Passwort: sharePasswordRequired"
|
|
e2e_expect "$before" "$(anna_link_count "/$FIX")" "Nextcloud hat keinen neuen Link (Passwort fehlte)"
|
|
|
|
pw="Tessera-E2E-$(python3 -I -c 'import secrets,string;print("".join(secrets.choice(string.ascii_letters+string.digits) for _ in range(16)))')"
|
|
code=$(call POST shares "{\"path\":\"/$FIX\",\"kind\":\"link\",\"access\":\"view\",\"password\":\"$pw\",\"expireDate\":\"\",\"label\":\"Kunde\"}")
|
|
e2e_expect 201 "$code" "Link mit starkem Passwort"
|
|
e2e_expect True "$(jq_py 'd["hasPassword"] is True and d["label"]=="Kunde"')" "Link: hasPassword, Bezeichnung"
|
|
if grep -qF -- "$pw" "$E2E_TMP/body.out"; then e2e_fail "das Passwort steht in der Antwort"; fi
|
|
id3=$(jq_py 'd["id"]')
|
|
urls="$urls $(jq_py 'd["url"]')"
|
|
|
|
code=$(call PUT "shares/$id3" '{"password":"abc"}')
|
|
e2e_expect 400 "$code" "schwaches Passwort beim Aendern"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"sharePasswordRejected"' "schwach: sharePasswordRejected"
|
|
e2e_expect True "$(jq_py 'bool(d.get("ncMessage"))')" "schwach: Meldung der Nextcloud vorhanden"
|
|
measured=$(nc_put "$id3" password abc)
|
|
echo "gemessen: Nextcloud antwortet auf PUT mit schwachem Passwort mit Status $measured"
|
|
|
|
code=$(call PUT "shares/$id3" '{"password":""}')
|
|
e2e_expect 400 "$code" "Passwort entfernen bei Pflicht"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"sharePasswordRequired"' "entfernen: sharePasswordRequired"
|
|
|
|
NC_OCC config:app:delete core shareapi_enforce_links_password >/dev/null
|
|
policy_wait 'd["links"]["passwordRequired"] is False' "Richtlinie: Passwort wieder freiwillig"
|
|
|
|
# --- Ablaufdatum wird von der Nextcloud verlangt (7 Tage) ---
|
|
NC_OCC config:app:set core shareapi_default_expire_date --value=true --type=boolean >/dev/null
|
|
NC_OCC config:app:set core shareapi_enforce_expire_date --value=true --type=boolean >/dev/null
|
|
NC_OCC config:app:set core shareapi_expire_after_n_days --value=7 >/dev/null
|
|
policy_wait 'd["links"]["expiryDefaultDays"]==7 and d["links"]["expiryEnforced"] is True' "Richtlinie: Ablauf 7 Tage verlangt"
|
|
|
|
before=$(anna_link_count "/$FIX")
|
|
code=$(call POST shares "{\"path\":\"/$FIX\",\"kind\":\"link\",\"access\":\"view\",\"expireDate\":\"\"}")
|
|
e2e_expect 400 "$code" "Link ohne Ablauf bei Pflicht"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"shareExpiryRequired"' "ohne Ablauf: shareExpiryRequired"
|
|
e2e_expect "$before" "$(anna_link_count "/$FIX")" "Nextcloud hat keinen neuen Link (Ablauf fehlte)"
|
|
|
|
plus3=$(date -u -d '+3 days' +%F)
|
|
plus30=$(date -u -d '+30 days' +%F)
|
|
code=$(call POST shares "{\"path\":\"/$FIX\",\"kind\":\"link\",\"access\":\"view\",\"expireDate\":\"$plus3\"}")
|
|
e2e_expect 201 "$code" "Link mit Ablauf in 3 Tagen"
|
|
e2e_expect "$plus3" "$(jq_py 'd["expiration"]')" "Link: Ablauf wie gesendet"
|
|
id4=$(jq_py 'd["id"]')
|
|
urls="$urls $(jq_py 'd["url"]')"
|
|
|
|
code=$(call POST shares "{\"path\":\"/$FIX\",\"kind\":\"link\",\"access\":\"view\",\"expireDate\":\"$plus30\"}")
|
|
echo "gemessen: Tessera antwortet auf ein neues Datum jenseits des Hoechstwerts mit Status $code"
|
|
e2e_expect 400 "$code" "Link mit Ablauf jenseits des Hoechstwerts"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"shareExpiryInvalid"' "zu spaet: shareExpiryInvalid"
|
|
|
|
code=$(call PUT "shares/$id4" "{\"expireDate\":\"$plus30\"}")
|
|
e2e_expect 400 "$code" "Ablauf jenseits des Hoechstwerts beim Aendern"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"shareExpiryInvalid"' "Aendern, zu spaet: shareExpiryInvalid"
|
|
measured=$(nc_put "$id4" expireDate "$plus30")
|
|
echo "gemessen: Nextcloud antwortet auf PUT mit Ablauf jenseits des Hoechstwerts mit Status $measured"
|
|
|
|
code=$(call PUT "shares/$id4" '{"expireDate":""}')
|
|
e2e_expect 400 "$code" "Ablauf entfernen bei Pflicht"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"shareExpiryRequired"' "Ablauf entfernen: shareExpiryRequired"
|
|
|
|
before=$(anna_link_count "/$FIX")
|
|
code=$(call POST shares "{\"path\":\"/$FIX\",\"kind\":\"link\",\"access\":\"view\",\"expireDate\":\"31.12.2026x\"}")
|
|
e2e_expect 400 "$code" "Datum in falschem Format"
|
|
e2e_expect "$before" "$(anna_link_count "/$FIX")" "Nextcloud hat keinen neuen Link (Datum ungueltig)"
|
|
|
|
for key in shareapi_default_expire_date shareapi_enforce_expire_date shareapi_expire_after_n_days; do
|
|
NC_OCC config:app:delete core "$key" >/dev/null
|
|
done
|
|
policy_wait 'd["links"]["expiryDefaultDays"] is None and d["links"]["expiryEnforced"] is False' "Richtlinie: Ablauf wieder frei"
|
|
|
|
# Uebersicht und Aufraeumen
|
|
code=$(call GET shares/mine)
|
|
e2e_expect 200 "$code" "Eigene Freigaben"
|
|
ids="$id1 $id2 $id3 $id4"
|
|
for id in $ids; do
|
|
e2e_expect True "$(jq_py 'any(s["id"]=="'"$id"'" and s["kind"]=="link" for s in d["shares"])')" "mine enthaelt Link $id"
|
|
done
|
|
for id in $ids; do
|
|
code=$(call DELETE "shares/$id")
|
|
e2e_expect 200 "$code" "Link $id loeschen"
|
|
done
|
|
e2e_expect 0 "$(anna_link_count "/$FIX")" "alle Links der Fixture sind weg"
|
|
|
|
# Weder Passwort noch Link-Adresse im Protokoll der API
|
|
local log
|
|
log=$(cd "$REPO" && docker compose logs api --since "$START_ISO" 2>&1 || true)
|
|
case "$log" in *"$pw"*) e2e_fail "das Passwort steht im Protokoll der API" ;; esac
|
|
for u in $urls; do
|
|
case "$log" in *"$u"*) e2e_fail "eine Link-Adresse steht im Protokoll der API" ;; esac
|
|
done
|
|
|
|
echo "e2e shares links ok"
|
|
}
|
|
|
|
section_received() {
|
|
local code id sfx_letters pid
|
|
|
|
# Eingehend und angenommen (Vorgabe der Nextcloud: sofort angenommen)
|
|
NC_OCC user:setting --delete anna files_sharing default_accept >/dev/null 2>&1 || true
|
|
code=$(ben_share_with_anna "$BENFIX")
|
|
e2e_expect 200 "$code" "ben teilt einen Ordner mit anna"
|
|
|
|
code=$(call GET shares/received)
|
|
e2e_expect 200 "$code" "Mit mir geteilt"
|
|
e2e_expect True "$(jq_py 'any(s["target"]=="/'"$BENFIX"'" and s["ownerName"]=="Ben Beispiel" and s["canEdit"] is False and s["kind"]=="user" for s in d["shares"])')" "received: Ordner von Ben Beispiel, nicht aenderbar"
|
|
id=$(jq_py '[s["id"] for s in d["shares"] if s["target"]=="/'"$BENFIX"'"][0]')
|
|
|
|
code=$(qget files path /)
|
|
e2e_expect 200 "$code" "Wurzel auflisten"
|
|
sfx_letters=$(jq_py '[e["permissions"] for e in d["entries"] if e["name"]=="'"$BENFIX"'"][0]')
|
|
echo "gemessen: Berechtigungsbuchstaben des eingehenden Ordners: $sfx_letters"
|
|
case "$sfx_letters" in *S*) ;; *) e2e_fail "eingehender Eintrag ohne Buchstaben S ($sfx_letters): Kennzeichen auf nc:mount-type umstellen" ;; esac
|
|
|
|
code=$(call GET shares/mine)
|
|
e2e_expect 200 "$code" "Eigene Freigaben"
|
|
e2e_expect True "$(jq_py 'not any(s["target"]=="/'"$BENFIX"'" or s["path"]=="/'"$BENFIX"'" for s in d["shares"])')" "mine enthaelt die eingehende Freigabe nicht"
|
|
|
|
# Verlassen (DELETE durch die Empfaengerin)
|
|
code=$(call DELETE "shares/$id")
|
|
e2e_expect 200 "$code" "Freigabe verlassen"
|
|
code=$(call GET shares/received)
|
|
e2e_expect True "$(jq_py 'not any(s["target"]=="/'"$BENFIX"'" for s in d["shares"]+d["pending"])')" "received: die verlassene Freigabe ist weg"
|
|
curl -s -u "$BEN" -H 'OCS-APIRequest: true' -H 'Accept: application/json' -G \
|
|
--data-urlencode "path=/$BENFIX" "$NC_OCS/shares" -o "$E2E_TMP/ben-after.out"
|
|
echo "gemessen: bens eigene Liste nach dem Verlassen: $(python3 -I -c 'import json,sys
|
|
d=json.load(open(sys.argv[1]))["ocs"]["data"]
|
|
print(len(d), "Freigaben", [(s.get("share_type"), s.get("share_with"), s.get("permissions")) for s in d])' "$E2E_TMP/ben-after.out")"
|
|
|
|
# Offene Freigabe: annas Vorgabe auf "nicht automatisch annehmen"
|
|
NC_OCC user:setting anna files_sharing default_accept no >/dev/null
|
|
code=$(ben_share_with_anna "$BENBOX")
|
|
e2e_expect 200 "$code" "ben teilt den Briefkasten mit anna"
|
|
code=$(call GET shares/received)
|
|
e2e_expect 200 "$code" "Mit mir geteilt (offen)"
|
|
e2e_expect True "$(jq_py 'any(s["name"].startswith("'"$BENBOX"'") and s.get("pending") is True for s in d["pending"]) and not any(s["name"].startswith("'"$BENBOX"'") for s in d["shares"])')" "received: offene Freigabe nur unter pending"
|
|
pid=$(jq_py '[s["id"] for s in d["pending"] if s["name"].startswith("'"$BENBOX"'")][0]')
|
|
|
|
code=$(call POST "shares/$pid/accept")
|
|
e2e_expect 200 "$code" "offene Freigabe annehmen"
|
|
e2e_expect True "$(jq_py 'd["accepted"] is True')" "accept: accepted"
|
|
code=$(call GET shares/received)
|
|
e2e_expect True "$(jq_py 'any(s["name"].startswith("'"$BENBOX"'") for s in d["shares"]) and not d["pending"]')" "received: angenommen, nichts mehr offen"
|
|
|
|
code=$(call DELETE "shares/$pid")
|
|
e2e_expect 200 "$code" "angenommene Freigabe verlassen"
|
|
code=$(call POST "shares/abc/accept")
|
|
e2e_expect 404 "$code" "annehmen mit ungueltiger Kennung"
|
|
e2e_contains "$E2E_TMP/body.out" '"code":"shareNotFound"' "accept abc: shareNotFound"
|
|
NC_OCC user:setting --delete anna files_sharing default_accept >/dev/null 2>&1 || true
|
|
|
|
echo "e2e shares received ok"
|
|
}
|
|
section_version() { echo "e2e shares version: folgt in Aufgabe 3"; }
|
|
|
|
case "$SECTION" in
|
|
people) section_people ;;
|
|
links) section_links ;;
|
|
received) section_received ;;
|
|
version) section_version ;;
|
|
all)
|
|
section_people
|
|
section_links
|
|
section_received
|
|
section_version
|
|
;;
|
|
esac
|
|
|
|
# Nie 401 oder 403 an den Browser (Fehlervertrag D-D)
|
|
if grep -qE '^(401|403)$' "$SEEN"; then e2e_fail "ein Aufruf endete mit 401 oder 403"; fi
|
|
: "$START_ISO"
|