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 |
|---|---|---|---|---|
| Configure o provisionamento SCIM 2.0 para sincronizar usuários e grupos do seu provedor de identidade com o SnapOtter. Abrange Okta, Azure AD / Entra ID e integrações personalizadas. | 06ee702b386e | human | e7fd4fb6bdad | 2 |
Provisionamento SCIM
O SnapOtter implementa o SCIM 2.0 (System for Cross-domain Identity Management) para o provisionamento automatizado de usuários e grupos. Seu provedor de identidade pode criar, atualizar, desativar e reativar contas de usuário e sincronizar associações a grupos automaticamente.
::: tip Recurso enterprise
O provisionamento SCIM requer uma licença enterprise com o recurso scim. Ele não está disponível no plano team. Sem o recurso, todos os endpoints SCIM (exceto discovery) retornam 403.
:::
Pré-requisitos
- Uma instância do SnapOtter em execução, acessível em uma URL pública
- Uma chave de licença enterprise com o recurso
scim - Uma conta SnapOtter
adminintegrada com seu conjunto completo de permissões efetivas. Uma função personalizada delegada ou uma chave de API de administrador sem qualquer permissão de administrador não pode gerar ou revogar o token SCIM global. - Acesso de administrador às configurações de provisionamento do seu provedor de identidade
Início rápido
- Gere um 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"
A resposta contém o token. Salve-o imediatamente; ele não pode ser recuperado novamente.
{
"token": "so_scim_v2_a1b2c3d4e5f6...",
"message": "Save this token - it cannot be retrieved again"
}
- No seu provedor de identidade, configure o provisionamento SCIM com:
- URL base:
https://photos.example.com/api/v1/scim/v2 - Autenticação: token bearer (cole o token da etapa 1)
- URL base:
Autenticação
Os endpoints SCIM usam um token Bearer dedicado, separado das sessões de usuário e das chaves de API.
Gerando um token
POST /api/v1/enterprise/scim/token gera um novo token SCIM. Como o token pode provisionar e alterar usuários em toda a instância, esse endpoint requer a função admin integrada com o conjunto completo de permissões de administrador efetivas. Manter users:manage em uma função personalizada não é suficiente.
O token é retornado em texto simples exatamente uma vez. O SnapOtter armazena apenas um hash scrypt. Se você perder o token, revogue-o e gere um novo.
Apenas um token SCIM fica ativo por vez. Gerar um novo token substitui o anterior.
::: warning Reemissão de token após atualização
Os tokens SCIM legados e não versionados são rejeitados. Após atualizar para uma versão que emita tokens so_scim_v2_..., gere um novo token e atualize seu provedor de identidade antes de retomar o provisionamento.
:::
Revogando um token
DELETE /api/v1/enterprise/scim/token revoga o token SCIM atual. Ele tem os mesmos requisitos de administração integrados completos da geração de token.
Limite de taxa
Os endpoints SCIM têm limite de taxa de 1000 requisições por minuto por token. Exceder esse limite retorna HTTP 429.
Recursos suportados
| Recurso SCIM | Conceito no SnapOtter | Criar | Ler | Atualizar | Excluir |
|---|---|---|---|---|---|
| User | Conta de usuário | Sim | Sim | Sim | Exclusão suave |
| Group | Team | Sim | Sim | Sim | Sim |
::: warning
Os Groups do SCIM mapeiam para teams do SnapOtter, não para papéis. O SCIM não pode definir o papel de um usuário. Todos os usuários criados via SCIM recebem o papel user. Para alterar o papel de um usuário, use a interface de administração do SnapOtter.
:::
Operações de usuário
Criar usuário
POST /api/v1/scim/v2/Users
Cria uma nova conta de usuário com authProvider definido como scim e o papel user. O usuário é atribuído ao team Default. Se active for false, o papel é definido como disabled em vez disso.
Atributos obrigatórios: userName. Opcionais: externalId, emails, active (padrão true).
Listar e filtrar usuários
GET /api/v1/scim/v2/Users
Retorna uma lista paginada de usuários. Suporta os parâmetros de consulta startIndex e count (máximo de 200 resultados por página).
A filtragem suporta apenas eq (igual), nestes atributos:
userName eq "jane"externalId eq "ext-12345"
Outros operadores de filtro e atributos retornam HTTP 400.
Obter usuário
GET /api/v1/scim/v2/Users/:id
Retorna um único usuário pelo seu ID de usuário no SnapOtter.
Substituir usuário
PUT /api/v1/scim/v2/Users/:id
Substitui os atributos do usuário. Suporta userName, externalId, emails e active. Alterações de nome de usuário são verificadas quanto a conflitos (409 se o novo nome de usuário já estiver em uso por outro usuário).
Aplicar patch em usuário
PATCH /api/v1/scim/v2/Users/:id
Atualização parcial usando SCIM PatchOp. Operações suportadas:
| Operação | Caminhos |
|---|---|
replace |
active, userName, externalId, emails, emails[type eq "work"].value, name.formatted, displayName |
add |
Igual a replace |
remove |
externalId, emails |
Os caminhos name.formatted e displayName são aceitos por compatibilidade, mas não têm efeito persistente (o SnapOtter não armazena um nome de exibição separado).
Operações replace sem valor (onde o valor é um objeto sem um path) também são suportadas, com as chaves userName, externalId, emails e active.
Desativar usuário (exclusão suave)
DELETE /api/v1/scim/v2/Users/:id
O SnapOtter não exclui usuários permanentemente via SCIM. Em vez disso, o DELETE realiza uma desativação suave:
- O papel do usuário é alterado do seu valor atual (por exemplo,
editor) paradisabled:editor, preservando o papel original. - A senha do usuário é apagada.
- Todas as sessões ativas são revogadas.
- Todas as chaves de API são revogadas.
O usuário não pode mais fazer login nem usar nenhuma chave de API. Seus dados (arquivos, histórico) são mantidos.
Reativar usuário
Para reativar um usuário previamente desativado, envie uma requisição PUT ou PATCH com active: true. O SnapOtter restaura o papel original de antes da desativação (por exemplo, disabled:editor volta a ser editor). Se o papel original não puder ser determinado, ele recorre a user.
::: details Exemplo: desativar e reativar via 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 }
]
}
:::
Operações de grupo
Os Groups do SCIM mapeiam para teams do SnapOtter. Criar um grupo cria um team. A associação a grupos controla a qual team um usuário pertence.
Criar grupo
POST /api/v1/scim/v2/Groups
Obrigatório: displayName. Opcional: members (array de { value: userId }).
Listar e filtrar grupos
GET /api/v1/scim/v2/Groups
A filtragem suporta apenas displayName eq "...". Paginado com startIndex e count (máximo de 200 resultados por página).
Obter grupo
GET /api/v1/scim/v2/Groups/:id
Substituir grupo
PUT /api/v1/scim/v2/Groups/:id
Substitui o nome do grupo e a lista completa de membros. Membros existentes que não estão na nova lista são movidos para o team Default.
Aplicar patch em grupo
PATCH /api/v1/scim/v2/Groups/:id
Suporta estas operações:
| Operação | Caminho | Efeito |
|---|---|---|
add |
members |
Adiciona usuários ao team |
remove |
members[value eq "userId"] |
Move o usuário para o team Default |
replace |
displayName |
Renomeia o team |
replace |
members |
Substitui todos os membros (membros removidos são movidos para o team Default) |
Excluir grupo
DELETE /api/v1/scim/v2/Groups/:id
Exclui o team. Todos os membros do team excluído são movidos para o team Default. Os usuários não são desativados nem excluídos.
Configuração do IdP
Okta
- No console de administração do Okta, abra seu aplicativo SnapOtter (ou crie um).
- Vá para a aba Provisioning e clique em Configure API Integration.
- Marque Enable API Integration e insira:
- Base URL:
https://photos.example.com/api/v1/scim/v2 - API Token: o token bearer SCIM gerado acima
- Base URL:
- Clique em Test API Credentials e depois em Save.
- Em Provisioning > To App, ative:
- Create Users
- Update User Attributes
- Deactivate Users
- Em Push Groups, configure quais grupos do Okta sincronizar como teams do SnapOtter.
Azure AD / Entra ID
- No portal do Azure, vá para seu aplicativo enterprise do SnapOtter.
- Vá para Provisioning e defina Provisioning Mode como Automatic.
- Em Admin Credentials, insira:
- Tenant URL:
https://photos.example.com/api/v1/scim/v2 - Secret Token: o token bearer SCIM gerado acima
- Tenant URL:
- Clique em Test Connection e depois em Save.
- Em Mappings, configure os mapeamentos de atributos de usuário e grupo. Os padrões geralmente funcionam, mas verifique se
userNamemapeia parauserPrincipalNameoumailconforme desejado. - Defina Provisioning Status como On e salve.
O Azure provisiona usuários e grupos em um ciclo de sincronização fixo (normalmente a cada 40 minutos).
Endpoints de discovery
Estes três endpoints estão disponíveis sem autenticação e descrevem as capacidades do servidor SCIM:
| Endpoint | Descrição |
|---|---|
GET /api/v1/scim/v2/ServiceProviderConfig |
Capacidades do servidor e recursos suportados |
GET /api/v1/scim/v2/Schemas |
Definições de schema de User e Group |
GET /api/v1/scim/v2/ResourceTypes |
Tipos de recurso disponíveis (User, Group) |
O ServiceProviderConfig anuncia estas capacidades:
| Recurso | Suportado |
|---|---|
| Patch | Sim |
| Bulk | Não |
| Filter | Sim (máx. 200 resultados, apenas o operador eq) |
| Change password | Não |
| Sort | Não |
| ETag | Não |
Limitações
- Filtragem: Apenas o operador
eqé suportado. Filtros complexos, os operadoresand/or,co(contém) esw(começa com) não são implementados. - Operações em lote: Não suportadas.
- Sort e ETag: Não suportados.
- Papéis: O SCIM não pode atribuir papéis do SnapOtter. Todos os usuários provisionados recebem o papel
user. - MAX_USERS: O limite da variável de ambiente
MAX_USERSnão é aplicado na criação de usuários via SCIM. Se você precisar limitar a contagem de usuários, gerencie as atribuições no seu IdP. - Um token: Apenas um token SCIM pode ficar ativo por vez. Se vários IdPs precisarem de acesso SCIM, eles precisam compartilhar o token.
- Grupos são teams: Os Groups do SCIM correspondem a teams, não a papéis ou grupos de permissão.
Solução de problemas
403 "SCIM provisioning requires an enterprise license with the scim feature"
Sua licença não inclui o recurso scim, ou nenhuma licença está configurada. O SCIM requer uma licença de plano enterprise. Verifique se SNAPOTTER_LICENSE_KEY está definido e se a licença inclui o recurso scim.
401 "Bearer token required"
A requisição SCIM não incluiu um cabeçalho Authorization: Bearer <token>. Verifique a configuração de provisionamento do seu IdP.
401 "Invalid token"
O token está malformado, usa o formato não versionado retirado ou não corresponde ao hash armazenado. Gere um token so_scim_v2_... atual e atualize o token nas configurações de provisionamento do seu IdP.
401 "SCIM not configured"
Nenhum token SCIM foi gerado ainda. Use o endpoint POST /api/v1/enterprise/scim/token para criar um.
409 "User already exists" / "userName already taken"
Já existe um usuário com o mesmo nome de usuário. Isso pode acontecer quando um IdP tenta novamente uma criação que falhou. Verifique se há nomes de usuário duplicados no painel de administração do SnapOtter.
429 "SCIM rate limit exceeded"
O IdP está enviando mais de 1000 requisições por minuto. Isso normalmente acontece durante uma grande sincronização inicial. A maioria dos IdPs tenta novamente automaticamente após a redefinição da janela de limite de taxa. Se o problema persistir, verifique o intervalo de sincronização de provisionamento do seu IdP.
Usuários desprovisionados, mas não removidos da interface
O DELETE do SCIM é uma desativação suave. Usuários desativados ainda aparecem na lista de usuários do administrador com um status desabilitado. Isso é intencional, para que seus dados sejam preservados. O papel deles aparece como disabled:<original-role>.