Logosulfite.app
rafagazani/sulfite 999999

Autorização por tenant

Como restringir permissões por usuário usando a view sulfite_authorized_users no banco do tenant

Autorização por tenant#

A autorização por tenant permite que o banco de cada tenant seja a fonte de verdade para restringir o que um usuário pode fazer no Sulfite. O token continua dizendo o teto de permissões; a view sulfite_authorized_users no banco do tenant aplica o recorte por usuário.

O banco do tenant nunca aumenta permissões. O resultado final é sempre a interseção entre permissões do token e permissões da view.

Quando usar#

Use este recurso quando:

  • cada tenant já tem usuários, cargos ou permissões no próprio banco;
  • o Sulfite deve refletir revogações imediatamente;
  • você quer evitar emitir um novo token toda vez que a permissão de um usuário muda;
  • um hub externo emite JWTs, mas a autorização fina fica nos bancos dos tenants.

Como funciona#

Em cada request autenticada, o servidor:

  1. resolve o tenant da request;
  2. encontra a primeira conexão database desse tenant;
  3. procura a view sulfite_authorized_users no schema configurado;
  4. consulta a linha pelo email do token;
  5. aplica tokenPermission && dbPermission em cada permissão.

can_edit_global é uma exceção proposital: ele vem sempre do token e nunca da view, porque o banco do tenant não pode conceder acesso ao namespace global do operador.

Ativação#

O recurso fica disponível quando o sulfite_server recebe um ServerConnectionRegistry e o tenant tem pelo menos uma conexão database.

Não há variável de ambiente específica para ligar a autorização por view. A ativação operacional é criar a view no banco do tenant:

CREATE VIEW sulfite_authorized_users AS
SELECT
  email,
  can_insert,
  can_edit,
  can_delete
FROM ...;

Se a conexão existe, mas a view ainda não foi criada, o Sulfite usa o fallback token-only. Isso permite migrar tenants gradualmente.

Contrato da view#

A view mínima deve expor estas colunas:

ColunaTipo esperadoObrigatóriaDescrição
email text Sim Email usado para casar com o token
can_insert boolean Sim Permite POST /reports
can_edit boolean Sim Permite PUT /reports/:id
can_delete boolean Sim Permite DELETE /reports/:id

Colunas opcionais também podem ser expostas:

ColunaDefault quando ausenteDescrição
can_listtrueLista relatórios
can_generatetrueGera outputs
can_settings false Gerencia conexões, tokens e introspecção
can_share false Cria links de compartilhamento
can_adminfalseAdmin multi-tenant

Colunas extras são ignoradas.

Não inclua `can_edit_global` na view. Mesmo que exista, o Sulfite não usa esse valor. Essa permissão só pode vir de um token emitido pelo operador ou IdP confiável.

Schema da view#

O schema é resolvido nesta ordem:

  1. extra.authSchema
  2. extra.schema
  3. public

Exemplo com a view no schema padrão:

{
  "name": "tenant-db",
  "type": "database",
  "host": "db.example.com",
  "port": 5432,
  "database": "tenant_acme",
  "username": "reader",
  "password": "secret",
  "ssl": true,
  "extra": { "schema": "public" }
}

Exemplo separando schema de dados e schema de autorização:

{
  "name": "tenant-db",
  "type": "database",
  "host": "db.example.com",
  "port": 5432,
  "database": "tenant_acme",
  "username": "reader",
  "password": "secret",
  "ssl": true,
  "extra": {
    "schema": "erp",
    "authSchema": "sulfite_meta"
  }
}

Nesse caso o Sulfite procura sulfite_meta.sulfite_authorized_users, enquanto os datasources podem continuar usando o schema erp.

Exemplo de view#

CREATE VIEW public.sulfite_authorized_users AS
SELECT
  u.email,
  p.can_create_reports AS can_insert,
  p.can_edit_reports AS can_edit,
  p.can_delete_reports AS can_delete,
  p.can_generate_reports AS can_generate,
  p.can_manage_reports AS can_settings
FROM users u
JOIN roles r ON r.id = u.role_id
JOIN report_permissions p ON p.role_id = r.id
WHERE u.active = true;

Uma linha ausente significa usuário não autorizado:

SituaçãoResultado
View não existeFallback para permissões do token
View existe e email não aparecePermissões negadas para o escopo da view
Token não tem email e a view existePermissões negadas
Erro de conexão ou queryRequest falha fechado com 503
authSchema inválidoErro de configuração

Tokens e email#

O lookup usa o campo email do token. Tokens emitidos pelo próprio Sulfite preenchem email a partir do subject informado em POST /tokens.

Para JWT RS256 via JWKS, o IdP deve emitir a claim email:

{
  "sub": "user-123",
  "email": "ana@example.com",
  "tenant_id": "tenant-acme",
  "permissions": {
    "reports": {
      "list": true,
      "generate": true,
      "create": true,
      "edit": true,
      "delete": false
    }
  }
}

Se a claim email estiver ausente, o Sulfite só consegue usar token-only enquanto a view não existir. Quando a view está presente, o usuário não identificável é negado.

Interseção de permissões#

Exemplo:

Permissão Token View Resultado
can_list true true true
can_insert true false false
can_edit false true false
can_delete true true true
can_edit_global true ignorado true

Isso permite um desenho simples:

  • o token define o teto que o operador ou IdP aceita conceder;
  • a view define o estado atual do usuário dentro do tenant;
  • revogar ou alterar a linha na view afeta a próxima request.

Checklist#

  • A conexão do tenant é do tipo database.
  • extra.authSchema ou extra.schema aponta para o schema correto, ou a view está em public.
  • A view contém email, can_insert, can_edit e can_delete.
  • O token HMAC ou JWT externo contém email.
  • A role de banco usada pelo Sulfite consegue fazer SELECT na view.
  • A view não expõe ou depende de can_edit_global.

Páginas relacionadas#