Como implementar row-level security em Lakehouse: passo a passo
Vamos implementar row-level security em Lakehouse para controlar que linhas cada utilizador ou equipa pode ver. Esta técnica é útil para cumprir requisitos de segurança e privacidade sem duplicar dados nem criar múltiplas tabelas por cliente. Ao filtrar a nível de linha, manténs uma única fonte de verdade (por exemplo, uma tabela Delta) e aplicas políticas de acesso de forma centralizada e auditável.
Pré-requisitos
- Conta com acesso ao Microsoft Fabric e permissão para criar tabelas no Lakehouse.
- Uma tabela Delta existente com coluna de segurança (ex.: tenant_id, region ou owner_id). Idealmente a coluna tem valores normalizados (ex.: T001, T002) e a tabela tem centenas a milhões de linhas.
- Acesso ao SQL analytics endpoint ou a um notebook com PySpark/SQL.
- Conceitos básicos de SQL e permissões no Fabric. Se tens uma equipa pequena, percorre a lista de papéis (roles) e identifica quem terá role_admins.
Passo 1: Escolher a coluna de controlo e criar uma tabela de mapeamento
Identifica a coluna que define visibilidade (por exemplo tenant_id). Esta coluna deve ser estável e eficiente para junções (indexável ou particionável). Cria uma tabela de mapeamento que relaciona utilizadores/grupos aos valores permitidos. Esta tabela funciona como a lista de controlo de acesso (ACL) e permite gerir permissões sem alterar a tabela principal.
CREATE TABLE IF NOT EXISTS security.mapping_users_tenants (
user_id STRING,
tenant_id STRING
);
-- Exemplo de inserção
INSERT INTO security.mapping_users_tenants VALUES
('alice@contoso.com','T001'),
('bob@contoso.com','T002'),
('group_sales','T001');
Exemplo prático: se a tua organização tem 50 tenants e 200 utilizadores, a tabela de mapping terá tipicamente algumas centenas de linhas. Mantém timestamps ou colunas de audit se precisares de rastrear quando um mapeamento foi alterado.
Passo 2: Criar uma view segura que filtra por utilizador
Cria uma view que usa o contexto do utilizador (CURRENT_USER ou função equivalente) para filtrar a tabela Delta principal por tenant_id. Assim, os utilizadores consultam a view em vez da tabela direta. As views tornam a política explícita e fácil de auditar.
CREATE OR REPLACE VIEW secure.v_orders AS
SELECT o.*
FROM lakehouse.orders o
JOIN security.mapping_users_tenants m
ON o.tenant_id = m.tenant_id
WHERE m.user_id = CURRENT_USER();
Notas: CURRENT_USER() costuma devolver o UPN (ex.: alice@contoso.com). Se usas grupos, adapta a lógica para verificar pertença a grupos (via uma tabela_groups ou uma função que expande grupos). Em ambientes com milhares de consultas, confirma que a view tem desempenho aceitável — uma junção em tenant_id é tipicamente O(N) sobre a partição relevante e costuma ser rápida quando as tabelas são particionadas adequadamente.
Passo 3: Testar a view com utilizadores simulados
Se não conseguires alterar identidades no ambiente de teste, podes substituir CURRENT_USER() por um valor literal ou criar views de teste. Testa com vários cenários: um utilizador com acesso a um tenant, outro com acesso a vários tenants, e um sem acessos. Conta as linhas esperadas: por exemplo, se orders tem 10.000 linhas e o tenant T001 tem 1.250 linhas, alice deve ver exactamente essas 1.250 linhas.
-- Teste substituindo CURRENT_USER()
SELECT * FROM secure.v_orders WHERE 1=1; -- como alice configurarás CURRENT_USER() no ambiente
-- Alternativa para testes
CREATE OR REPLACE VIEW secure.v_orders_test AS
SELECT o.*
FROM lakehouse.orders o
JOIN security.mapping_users_tenants m
ON o.tenant_id = m.tenant_id
WHERE m.user_id = 'alice@contoso.com';
Executa testes automatizados: por exemplo, um conjunto de 10 queries que validam contagens e amostras (SELECT COUNT(*) e SELECT TOP 10). Documenta resultados esperados para regressão.
Passo 4: Restringir acessos diretos à tabela base
É importante impedir que utilizadores leiam diretamente a tabela Delta. Revoga permissões de SELECT na tabela base e concede apenas à equipa de administradores; dá permissão de SELECT apenas na view segura. Sem esta etapa, um utilizador privilegiado poderia contornar a view.
-- Revogar acesso geral
REVOKE SELECT ON TABLE lakehouse.orders FROM PUBLIC;
-- Conceder apenas a admins
GRANT SELECT ON TABLE lakehouse.orders TO role_admins;
-- Conceder acesso à view a utilizadores
GRANT SELECT ON VIEW secure.v_orders TO PUBLIC;
Verifica sempre as permissões com consultas administrativas (por exemplo, SHOW GRANTS). Para auditoria, regista quem tem papel role_admins; idealmente limita esse papel a menos de 5 administradores.
Passo 5: Automatizar actualizações de mapping (opcional)
Para ambientes dinâmicos, cria um processo ETL/ELT que actualize security.mapping_users_tenants a partir de Azure AD ou outro sistema de identidade. Um notebook simples com PySpark pode sincronizar mudanças quotidianamente às 01:00, demorando tipicamente 1–5 minutos para tabelas de algumas milhares de linhas.
# Exemplo PySpark para atualizar mapping (esquema mínimo)
mapping_df = spark.read.format('delta').load('/mnt/security/mapping_users_tenants')
# Código para obter dados de identidade e escrever de volta
new_df.write.format('delta').mode('overwrite').save('/mnt/security/mapping_users_tenants')
Considera usar incremental updates em vez de overwrite se tens muitos registos. Regista alterações e envia alertas quando um mapping crítico muda (por exemplo, remoção de acesso de um administrador).
Verificar o resultado
Para confirmar que a row-level security funciona: 1) Inicia sessão como utilizador A e executa SELECT na view secure.v_orders — só deves ver linhas do tenant de A; valida com COUNT(*). 2) Tenta SELECT directo em lakehouse.orders com o mesmo utilizador — deve ser negado. 3) Testa com outro utilizador/grupo para garantir isolamento. Faz testes de carga básicos (por exemplo 50 queries concorrentes) para assegurar que a view não se torna um gargalo.
Conclusão
Com uma coluna de controlo, uma tabela de mapping e views filtradas consegues implementar row-level security em Lakehouse de forma simples, auditável e escalável. Próximos passos: integrar com Azure AD para gestão automática de grupos, usar políticas dinâmicas baseadas em funções, e incluir testes automatizados com contas de teste. Dica: documenta os mapeamentos, regista alterações e testa regularmente para evitar fugas de dados.