A release-readiness QA pass over the whole product. The commits split into defects a user would hit and gates that were reporting green while measuring nothing. ## Fixes that change behaviour Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so request.ip came from a client-set header and a forged X-Forwarded-For got past the login limiter. The default is now a private-network trust list. A transient Postgres outage stranded in-flight jobs, leaving finished output on disk with no row pointing at it. A reconciler now resolves those rows and adopts the bytes rather than dropping the work. A Redis connection that moved to a new address wedged every read-blocked consumer, so completions stopped signalling while health still answered 200. Socket timeouts plus subscriber pings recover it. Installing more than one AI bundle left the shared venv multi-versioned and silently broke three tools. The installer now reconciles distributions to one version each. Converting an image to JXL at quality 1 through 4 returned a 500, because libjxl 0.7 rejects the distance those values compute. The quality is floored at what the encoder honours. A missing ffmpeg was also reported to the user as a corrupt upload; it now says the engine is unavailable. RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at 0.22.2, and the release scan was split so it can fail on an unfixed critical instead of hiding it behind ignore-unfixed. ## Gates that could not fail Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs build; coverage discarded its whole report on any failing test; the lint gate skipped root tests, scripts, and two workspaces; and several generated matrices counted a host missing ffmpeg as a passing tool. Each now measures what it claims. Full evidence and the outstanding release items are tracked locally and are not part of this branch.
14 KiB
description, i18n_source_hash, i18n_provenance, i18n_output_hash, i18n_hash_version
| description | i18n_source_hash | i18n_provenance | i18n_output_hash | i18n_hash_version |
|---|---|---|---|---|
| Skonfiguruj provisioning SCIM 2.0, aby synchronizować użytkowników i grupy z Twojego dostawcy tożsamości do SnapOtter. Obejmuje Okta, Azure AD / Entra ID oraz integracje niestandardowe. | 06ee702b386e | human | 2c93409c4926 | 2 |
Provisioning SCIM
SnapOtter implementuje SCIM 2.0 (System for Cross-domain Identity Management) do automatycznego provisioningu użytkowników i grup. Twój dostawca tożsamości może automatycznie tworzyć, aktualizować, dezaktywować i ponownie aktywować konta użytkowników oraz synchronizować członkostwo w grupach.
::: tip Funkcja enterprise
Provisioning SCIM wymaga licencji enterprise z funkcją scim. Nie jest dostępny w planie team. Bez tej funkcji wszystkie punkty końcowe SCIM (poza discovery) zwracają 403.
:::
Wymagania wstępne
- Działająca instancja SnapOtter dostępna pod publicznym adresem URL
- Klucz licencji enterprise z funkcją
scim - Wbudowane konto SnapOtter
adminz pełnym efektywnym zestawem uprawnień. Delegowana rola niestandardowa lub klucz API administratora, któremu brakuje uprawnień administratora, nie mogą wygenerować ani unieważnić globalnego tokena SCIM. - Dostęp administracyjny do ustawień provisioningu Twojego dostawcy tożsamości
Szybki start
- Wygeneruj token bearer SCIM:
curl -X POST https://photos.example.com/api/v1/enterprise/scim/token \
-H "Cookie: snapotter-session=YOUR_SESSION" \
-H "Content-Type: application/json"
Odpowiedź zawiera token. Zapisz go natychmiast; nie można go pobrać ponownie.
{
"token": "so_scim_v2_a1b2c3d4e5f6...",
"message": "Save this token - it cannot be retrieved again"
}
- W swoim dostawcy tożsamości skonfiguruj provisioning SCIM za pomocą:
- Base URL:
https://photos.example.com/api/v1/scim/v2 - Uwierzytelnianie: token bearer (wklej token z kroku 1)
- Base URL:
Uwierzytelnianie
Punkty końcowe SCIM używają dedykowanego tokenu Bearer, odrębnego od sesji użytkowników i kluczy API.
Generowanie tokenu
POST /api/v1/enterprise/scim/token generuje nowy token SCIM. Ponieważ token może udostępniać i mutować użytkowników w całej instancji, ten punkt końcowy wymaga wbudowanej roli admin z pełnym efektywnym zestawem uprawnień administratora. Trzymanie users:manage w roli niestandardowej nie jest wystarczające.
Token jest zwracany w postaci jawnej dokładnie raz. SnapOtter przechowuje tylko hash scrypt. Jeśli utracisz token, odwołaj go i wygeneruj nowy.
Jednocześnie aktywny jest tylko jeden token SCIM. Wygenerowanie nowego tokenu zastępuje poprzedni.
::: warning Ponowne wydanie tokena po aktualizacji
Starsze, niewersjonowane tokeny SCIM są odrzucane. Po uaktualnieniu do wersji, która wystawia tokeny so_scim_v2_..., wygeneruj nowy token i zaktualizuj dostawcę tożsamości przed wznowieniem udostępniania.
:::
Odwoływanie tokenu
DELETE /api/v1/enterprise/scim/token unieważnia bieżący token SCIM. Ma te same pełne wbudowane wymagania administracyjne, co generowanie tokenów.
Ograniczanie liczby żądań
Punkty końcowe SCIM mają limit 1000 żądań na minutę na token. Przekroczenie tego limitu zwraca HTTP 429.
Obsługiwane zasoby
| Zasób SCIM | Pojęcie w SnapOtter | Tworzenie | Odczyt | Aktualizacja | Usuwanie |
|---|---|---|---|---|---|
| User | Konto użytkownika | Tak | Tak | Tak | Miękkie usunięcie |
| Group | Zespół | Tak | Tak | Tak | Tak |
::: warning
Grupy SCIM są mapowane na zespoły SnapOtter, a nie na role. SCIM nie może ustawić roli użytkownika. Wszyscy użytkownicy utworzeni przez SCIM otrzymują rolę user. Aby zmienić rolę użytkownika, użyj interfejsu administracyjnego SnapOtter.
:::
Operacje na użytkownikach
Tworzenie użytkownika
POST /api/v1/scim/v2/Users
Tworzy nowe konto użytkownika z authProvider ustawionym na scim i rolą user. Użytkownik zostaje przypisany do zespołu Default. Jeśli active ma wartość false, rola jest zamiast tego ustawiana na disabled.
Wymagane atrybuty: userName. Opcjonalne: externalId, emails, active (domyślnie true).
Wyświetlanie i filtrowanie użytkowników
GET /api/v1/scim/v2/Users
Zwraca stronicowaną listę użytkowników. Obsługuje parametry zapytania startIndex i count (maksymalnie 200 wyników na stronę).
Filtrowanie obsługuje tylko eq (równa się) na tych atrybutach:
userName eq "jane"externalId eq "ext-12345"
Inne operatory filtrów i atrybuty zwracają HTTP 400.
Pobieranie użytkownika
GET /api/v1/scim/v2/Users/:id
Zwraca pojedynczego użytkownika według jego identyfikatora użytkownika SnapOtter.
Zastępowanie użytkownika
PUT /api/v1/scim/v2/Users/:id
Zastępuje atrybuty użytkownika. Obsługuje userName, externalId, emails oraz active. Zmiany nazwy użytkownika są sprawdzane pod kątem konfliktów (409, jeśli nowa nazwa użytkownika jest zajęta przez innego użytkownika).
Częściowa aktualizacja użytkownika
PATCH /api/v1/scim/v2/Users/:id
Częściowa aktualizacja przy użyciu SCIM PatchOp. Obsługiwane operacje:
| Operacja | Ścieżki |
|---|---|
replace |
active, userName, externalId, emails, emails[type eq "work"].value, name.formatted, displayName |
add |
Tak samo jak replace |
remove |
externalId, emails |
Ścieżki name.formatted i displayName są akceptowane w celu zachowania zgodności, ale nie mają trwałego efektu (SnapOtter nie przechowuje osobnej nazwy wyświetlanej).
Obsługiwane są także operacje replace bez wartości (gdzie wartość jest obiektem bez path), z kluczami userName, externalId, emails oraz active.
Dezaktywacja użytkownika (miękkie usunięcie)
DELETE /api/v1/scim/v2/Users/:id
SnapOtter nie usuwa użytkowników trwale przez SCIM. Zamiast tego DELETE wykonuje miękką dezaktywację:
- Rola użytkownika zostaje zmieniona z jej bieżącej wartości (np.
editor) nadisabled:editor, zachowując oryginalną rolę. - Hasło użytkownika zostaje wyczyszczone.
- Wszystkie aktywne sesje zostają odwołane.
- Wszystkie klucze API zostają odwołane.
Użytkownik nie może już się logować ani używać żadnych kluczy API. Jego dane (pliki, historia) są zachowywane.
Ponowna aktywacja użytkownika
Aby ponownie aktywować wcześniej dezaktywowanego użytkownika, wyślij żądanie PUT lub PATCH z active: true. SnapOtter przywraca oryginalną rolę sprzed dezaktywacji (np. disabled:editor ponownie staje się editor). Jeśli oryginalnej roli nie da się ustalić, następuje powrót do user.
::: details Przykład: dezaktywacja i ponowna aktywacja przez PATCH
// Deactivate
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{ "op": "replace", "path": "active", "value": false }
]
}
// Reactivate
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{ "op": "replace", "path": "active", "value": true }
]
}
:::
Operacje na grupach
Grupy SCIM są mapowane na zespoły SnapOtter. Utworzenie grupy tworzy zespół. Członkostwo w grupie steruje tym, do którego zespołu należy użytkownik.
Tworzenie grupy
POST /api/v1/scim/v2/Groups
Wymagane: displayName. Opcjonalne: members (tablica { value: userId }).
Wyświetlanie i filtrowanie grup
GET /api/v1/scim/v2/Groups
Filtrowanie obsługuje tylko displayName eq "...". Stronicowane za pomocą startIndex i count (maksymalnie 200 wyników na stronę).
Pobieranie grupy
GET /api/v1/scim/v2/Groups/:id
Zastępowanie grupy
PUT /api/v1/scim/v2/Groups/:id
Zastępuje nazwę grupy i pełną listę członków. Istniejący członkowie, których nie ma na nowej liście, zostają przeniesieni do zespołu Default.
Częściowa aktualizacja grupy
PATCH /api/v1/scim/v2/Groups/:id
Obsługuje te operacje:
| Operacja | Ścieżka | Efekt |
|---|---|---|
add |
members |
Dodaje użytkowników do zespołu |
remove |
members[value eq "userId"] |
Przenosi użytkownika do zespołu Default |
replace |
displayName |
Zmienia nazwę zespołu |
replace |
members |
Zastępuje wszystkich członków (usunięci członkowie zostają przeniesieni do zespołu Default) |
Usuwanie grupy
DELETE /api/v1/scim/v2/Groups/:id
Usuwa zespół. Wszyscy członkowie usuniętego zespołu zostają przeniesieni do zespołu Default. Użytkownicy nie są dezaktywowani ani usuwani.
Konfiguracja IdP
Okta
- W konsoli administracyjnej Okta otwórz swoją aplikację SnapOtter (lub ją utwórz).
- Przejdź do zakładki Provisioning i kliknij Configure API Integration.
- Zaznacz Enable API Integration i wprowadź:
- Base URL:
https://photos.example.com/api/v1/scim/v2 - API Token: wygenerowany powyżej token bearer SCIM
- Base URL:
- Kliknij Test API Credentials, a następnie Save.
- W sekcji Provisioning > To App włącz:
- Create Users
- Update User Attributes
- Deactivate Users
- W sekcji Push Groups skonfiguruj, które grupy Okta mają być synchronizowane jako zespoły SnapOtter.
Azure AD / Entra ID
- W portalu Azure przejdź do swojej aplikacji korporacyjnej SnapOtter.
- Przejdź do Provisioning i ustaw Provisioning Mode na Automatic.
- W sekcji Admin Credentials wprowadź:
- Tenant URL:
https://photos.example.com/api/v1/scim/v2 - Secret Token: wygenerowany powyżej token bearer SCIM
- Tenant URL:
- Kliknij Test Connection, a następnie Save.
- W sekcji Mappings skonfiguruj mapowania atrybutów użytkowników i grup. Wartości domyślne zwykle działają, ale sprawdź, czy
userNamejest zgodnie z oczekiwaniami mapowany nauserPrincipalNamelubmail. - Ustaw Provisioning Status na On i zapisz.
Azure prowadzi provisioning użytkowników i grup w stałym cyklu synchronizacji (zwykle co 40 minut).
Punkty końcowe discovery
Te trzy punkty końcowe są dostępne bez uwierzytelniania i opisują możliwości serwera SCIM:
| Punkt końcowy | Opis |
|---|---|
GET /api/v1/scim/v2/ServiceProviderConfig |
Możliwości serwera i obsługiwane funkcje |
GET /api/v1/scim/v2/Schemas |
Definicje schematów User i Group |
GET /api/v1/scim/v2/ResourceTypes |
Dostępne typy zasobów (User, Group) |
ServiceProviderConfig ogłasza te możliwości:
| Funkcja | Obsługiwane |
|---|---|
| Patch | Tak |
| Bulk | Nie |
| Filter | Tak (maks. 200 wyników, tylko operator eq) |
| Zmiana hasła | Nie |
| Sort | Nie |
| ETag | Nie |
Ograniczenia
- Filtrowanie: obsługiwany jest tylko operator
eq. Filtry złożone, operatoryand/or,co(zawiera) orazsw(zaczyna się od) nie są zaimplementowane. - Operacje zbiorcze: nieobsługiwane.
- Sort i ETag: nieobsługiwane.
- Role: SCIM nie może przypisywać ról SnapOtter. Wszyscy provisionowani użytkownicy otrzymują rolę
user. - MAX_USERS: limit zmiennej środowiskowej
MAX_USERSnie jest egzekwowany przy tworzeniu użytkowników przez SCIM. Jeśli musisz ograniczyć liczbę użytkowników, zarządzaj przypisaniami w swoim IdP. - Jeden token: jednocześnie aktywny może być tylko jeden token SCIM. Jeśli wiele IdP potrzebuje dostępu SCIM, muszą współdzielić ten token.
- Grupy to zespoły: grupy SCIM odpowiadają zespołom, a nie rolom czy grupom uprawnień.
Rozwiązywanie problemów
403 "SCIM provisioning requires an enterprise license with the scim feature"
Twoja licencja nie zawiera funkcji scim albo nie skonfigurowano żadnej licencji. SCIM wymaga licencji planu enterprise. Sprawdź, czy SNAPOTTER_LICENSE_KEY jest ustawione, a licencja zawiera funkcję scim.
401 "Bearer token required"
Żądanie SCIM nie zawierało nagłówka Authorization: Bearer <token>. Sprawdź konfigurację provisioningu w swoim IdP.
401 "Invalid token"
Token jest zniekształcony, używa wycofanego formatu niewersjonowanego lub nie pasuje do zapisanego skrótu. Wygeneruj aktualny token so_scim_v2_... i zaktualizuj token w ustawieniach udostępniania IdP.
401 "SCIM not configured"
Nie wygenerowano jeszcze żadnego tokenu SCIM. Użyj punktu końcowego POST /api/v1/enterprise/scim/token, aby go utworzyć.
409 "User already exists" / "userName already taken"
Użytkownik o tej samej nazwie użytkownika już istnieje. Może się to zdarzyć, gdy IdP ponawia nieudane utworzenie. Sprawdź, czy w panelu administracyjnym SnapOtter nie ma zduplikowanych nazw użytkowników.
429 "SCIM rate limit exceeded"
IdP wysyła ponad 1000 żądań na minutę. Zwykle dzieje się tak podczas dużej synchronizacji początkowej. Większość IdP automatycznie ponawia próbę po zresetowaniu okna limitu. Jeśli problem się utrzymuje, sprawdź interwał synchronizacji provisioningu swojego IdP.
Użytkownicy zostali deprovisioned, ale nie usunięto ich z interfejsu
SCIM DELETE to miękka dezaktywacja. Dezaktywowani użytkownicy nadal pojawiają się na liście użytkowników administratora ze statusem wyłączony. Jest to działanie zamierzone, aby zachować ich dane. Ich rola jest wyświetlana jako disabled:<original-role>.