#!/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 prueft den Modul-Changelog (bleibt 1.0.0). # 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 (10 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" BENDECL="Ben-Ablehnen-$EPOCH" RESHARE="Weitergabe-$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 curl -s -o /dev/null -u "$BEN" -X DELETE "$NC_DAV/files/ben/$BENDECL" 2>/dev/null curl -s -o /dev/null -u "$ANNA" -X DELETE "$NC_DAV/files/anna/$RESHARE" 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 [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 [ ...] — 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 — 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 — 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 — 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 — 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 folder_letters pid2 # 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 # Die Liste nennt die Buchstaben des Ordners selbst: eigene Wurzel darf anlegen, ein nur zum # Ansehen geteilter Ordner nicht (Dateiansicht blendet "Neuer Ordner" und "Hochladen" damit aus). e2e_expect True "$(jq_py 'all(c in (d.get("permissions") or "") for c in "CK")')" "Liste der Wurzel: Buchstaben C und K" code=$(qget files path "/$BENFIX") e2e_expect 200 "$code" "eingehenden Ordner auflisten" folder_letters=$(jq_py 'd.get("permissions")') echo "gemessen: Buchstaben des geoeffneten Nur-Ansehen-Ordners: $folder_letters" e2e_expect True "$(jq_py '"C" not in (d.get("permissions") or "x") and "K" not in (d.get("permissions") or "x") and d.get("permissions") is not None')" "Nur-Ansehen-Ordner: weder C noch K" 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" # Offene Freigabe ablehnen: gemessen (Nextcloud 34.0.4) geht das ueber DELETE shares/{id} # (200, danach weder offen noch angenommen); DELETE shares/pending/{id} ergibt dort 405. code=$(ben_share_with_anna "$BENDECL") e2e_expect 200 "$code" "ben teilt einen Ordner zum Ablehnen" code=$(call GET shares/received) e2e_expect True "$(jq_py 'any(s["name"].startswith("'"$BENDECL"'") and s.get("pending") is True for s in d["pending"])')" "received: Ablehnen-Ordner ist offen" pid2=$(jq_py '[s["id"] for s in d["pending"] if s["name"].startswith("'"$BENDECL"'")][0]') code=$(call DELETE "shares/$pid2") e2e_expect 200 "$code" "offene Freigabe ablehnen" code=$(call GET shares/received) e2e_expect True "$(jq_py 'not any(s["name"].startswith("'"$BENDECL"'") for s in d["shares"]+d["pending"])')" "received: abgelehnte Freigabe ist weg" curl -s -u "$BEN" -H 'OCS-APIRequest: true' -H 'Accept: application/json' -G \ --data-urlencode "path=/$BENDECL" "$NC_OCS/shares" -o "$E2E_TMP/ben-decl.out" echo "gemessen: bens Liste nach dem Ablehnen: $(python3 -I -c 'import json,sys print(len(json.load(open(sys.argv[1]))["ocs"]["data"]), "Freigaben")' "$E2E_TMP/ben-decl.out")" # Weitergabe und Pfade mit doppeltem Leerzeichen: anna teilt / an ben (mit Weitergabe- # recht), ben gibt den Unterordner "Sub X" an die Gruppe weiter. Fuer anna (Dateieigentuemerin) # nennt die Pfadliste die Weitergabe von Ben, der Pfad bleibt woertlich (zwei Leerzeichen). curl -s -o /dev/null -u "$ANNA" -X MKCOL "$NC_DAV/files/anna/$RESHARE" curl -s -o /dev/null -u "$ANNA" -X MKCOL "$NC_DAV/files/anna/$RESHARE/Sub%20%20X" curl -s -o /dev/null -u "$ANNA" -H 'OCS-APIRequest: true' -H 'Accept: application/json' -X POST \ --data-urlencode "path=/$RESHARE" -d shareType=0 -d shareWith=ben -d permissions=31 "$NC_OCS/shares" code=$(curl -s -o /dev/null -w '%{http_code}' -u "$BEN" -H 'OCS-APIRequest: true' -H 'Accept: application/json' -X POST \ --data-urlencode "path=/$RESHARE/Sub X" -d shareType=1 -d shareWith=tessera-team -d permissions=1 "$NC_OCS/shares") e2e_expect 200 "$code" "ben gibt den Unterordner weiter" code=$(qget shares/by-path path "/$RESHARE/Sub X") e2e_expect 200 "$code" "Pfadliste des Unterordners mit zwei Leerzeichen" e2e_expect True "$(jq_py 'len(d["shares"])==1 and d["shares"][0]["path"]=="/'"$RESHARE"'/Sub X" and d["shares"][0]["ownerId"]=="ben" and d["shares"][0]["kind"]=="group"')" "Weitergabe: Pfad woertlich, Freigebender ben" 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() { local code code=$(e2e_status "$ADMIN" GET "$API/modules/changelog/nextcloud-files") e2e_expect 200 "$code" "Modul-Changelog" e2e_expect 1.0.0 "$(jq_py 'd[0]["version"]')" "Modul-Changelog: neueste Version bleibt 1.0.0" e2e_expect True "$(jq_py 'any("Öffentliche Links" in c["de"] for c in d[0]["changes"])')" "Modul-Changelog: Punkt zu oeffentlichen Links" echo "e2e shares version ok" } 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"