CDS Access Control (DCL) na prática: o guia completo de controle de acesso por linha no ABAP
Quem decide quais linhas o usuário enxerga?
No ABAP clássico, filtrar dados por autorização significava espalhar AUTHORITY-CHECK e cláusulas WHERE manuais por todo lugar — fácil de esquecer, difícil de manter. O CDS Access Control resolve isso de forma declarativa: você descreve a regra uma vez, em DCL (Data Control Language), e o kernel a aplica implicitamente em toda leitura da entidade.
A ideia central: uma CDS role não é atribuída a usuários — ela é avaliada para cada usuário a cada acesso, e o motor injeta um WHERE derivado das condições que você definiu. O resultado vale uniformemente em OData, EML, ABAP SQL, analytics — qualquer consumidor da CDS.
Este é um guia de referência completo do DCL: a annotation que liga a checagem, os tipos de condição (PFCG, user, literal), herança entre views, COMBINATION MODE, REDEFINITION e o bypass via PRIVILEGED ACCESS — com exemplos e as armadilhas que mais derrubam gente.
O essencial em 4 linhas
DCL filtra linhas na leitura, declarativamente. A CDS role é avaliada para todo usuário, não atribuída a ninguém.
A checagem só vale no acesso direto à entidade que carrega a DCL — não se propaga para quem faz
SELECT FROMela.PFCG condition integra objetos de autorização clássicos; user condition filtra "meus registros"; literal compara com valores fixos/sessão.
DCL cobre leitura; modificação (create/update/delete) fica na Behavior Definition do RAP.
O que é CDS Access Control e quando aplicá-lo?
CDS Access Control restringe implicitamente as linhas retornadas por uma CDS entity quando ela é acessada via ABAP SQL. A checagem acontece na camada do modelo de dados, complementando — e em muitos cenários substituindo — os clássicos AUTHORITY-CHECK (SAP Help — ABAP CDS Access Control). Suportam DCL: CDS Views, View Entities, Hierarchies, Table Functions e Transactional Queries.
A regra que pega todo mundo: a checagem é aplicada somente no acesso direto à entidade que possui a DCL. Segundo a documentação, "quando uma CDS entity usada como data source em outras entidades é acessada indiretamente, nenhum controle de acesso implícito é aplicado". Ou seja: uma view #NOT_ALLOWED consumida por outra #CHECK bypassa a proteção, porque o motor só checa a entidade consultada diretamente.
Como a annotation @AccessControl.authorizationCheck controla a checagem?
A annotation @AccessControl.authorizationCheck define o comportamento da entidade frente ao controle de acesso. O valor padrão é #CHECK. Há um detalhe que confunde muita gente: a diferença entre #CHECK e #NOT_REQUIRED é só o aviso de ativação, não "ligado vs. desligado".
Valor | O que faz | Quando usar |
|---|---|---|
| Aplica a DCL se existir; emite aviso na ativação se não houver role. | Entidades expostas a usuários finais. |
| Não desliga o controle — só suprime o aviso. Se houver role, ela é aplicada. | Views auxiliares onde a DCL é opcional. |
| Exige que exista uma DCL; do contrário, erro. | Entidades sensíveis (HR, financeiro). |
| Desliga de fato o controle; roles são ignoradas. | Views técnicas privadas, nunca expostas. |
| Só pode ser lida com | Entidades de acesso exclusivamente privilegiado. |
@AccessControl.authorizationCheck: #CHECK
define view entity ZI_SalesOrder
as select from vbak
{
key vbeln as SalesOrder,
auart as SalesOrderType,
vkorg as SalesOrganization,
netwr as NetAmount
}Mito corrigido: #NOT_REQUIRED não abre vazamento de dados por si só — ele só silencia o aviso de "role ausente". O risco real é indireto: ao remover o lembrete, você pode esquecer de criar a role, e sem role não há filtro. Quem desliga de fato o controle é #NOT_ALLOWED.
Como é a estrutura de uma DEFINE ROLE?
Uma define role é o container das regras de acesso. Ela pode conter várias grant select on para entidades distintas, e a mesma entidade pode aparecer em várias regras ou várias roles — combinadas por OR por padrão. Lembre: a role é avaliada para todo usuário, não atribuída a ninguém.
Existem dois tipos de regra. A full access rule (sem WHERE) concede acesso irrestrito — use só em dados públicos, como tabelas de customizing (códigos de país, moeda):
define role ZI_CONFIG_TABLE_FULL {
grant select on ZI_ConfigTable; // acesso pleno a todos
}A conditional access rule (com WHERE) é a forma comum: restringe linhas por condições, que podem combinar literal, PFCG e user com AND/OR:
define role ZI_SALES_ORDER {
grant select on ZI_SalesOrder
where ( SalesOrganization = '1010' OR CurrencyCode = 'EUR' )
and CreatedOn between '20240101' and '20241231';
}Os operadores de uma literal condition incluem =, <>, <, >, [NOT] BETWEEN, [NOT] LIKE e IS [NOT] NULL. Você também compara com variáveis de sessão, útil para multi-cliente:
define role ZI_CLIENT_DATA {
grant select on ZI_SalesOrder
where Client = $session.client;
}Como filtrar por autorização PFCG (aspect pfcg_auth)?
A PFCG condition é a integração nativa com os objetos de autorização clássicos (criados em SU21). É o padrão para qualquer dado de negócio em S/4HANA com governança de roles já existente. A regra de ouro da sintaxe: a quantidade de elementos CDS à esquerda deve coincidir com a de campos mapeados à direita, na mesma ordem — o mapeamento é posicional (SAP Help — pfcg_condition).
@MappingRole: true
@EndUserText.label: 'DCL para ZI_SalesOrder'
define role ZI_SALES_ORDER {
grant select on ZI_SalesOrder
where
( SalesOrderType ) =
aspect pfcg_auth( V_VBAK_AAT, AUART, ACTVT = '03' )
and
( OrganizationDivision, SalesOrganization, DistributionChannel ) =
aspect pfcg_auth( V_VBAK_VKO, SPART, VKORG, VTWEG, ACTVT = '03' );
}Só voltam as linhas para as quais o usuário tem autorização em ambos os objetos. Há variações úteis:
Gate booleano (parênteses vazios à esquerda): não filtra linhas — é tudo ou nada. Se o usuário tiver ao menos uma autorização no objeto, a condição é verdadeira.
Mesmo campo dos dois lados: usa os valores da autorização como filtro direto.
// Gate: acesso pleno ou nenhum, sem filtrar linhas
where ( ) = aspect pfcg_auth( S_DEVELOP, ACTVT = '03' );
// Mesmo campo: filtra pelos valores autorizados
where ( CarrierId ) = aspect pfcg_auth( S_CARRID, CARRID, CARRID = 'LH' );Correção importante sobre o operador ?=: muita fonte (e muito tutorial) afirma que ?= significa "se o usuário não tiver autorização nenhuma, libera acesso pleno". Isso está errado. Segundo a documentação oficial, ?= funciona como o =, mas a condição também é satisfeita quando os elementos CDS à esquerda têm valor NULL ou inicial. É um operador de tolerância a NULL/inicial — não um override de "autorização ausente".
Como filtrar pelos registros do próprio usuário (aspect user)?
A user condition compara um elemento da CDS com o usuário logado — o equivalente de comparar com sy-uname, mas avaliado pelo motor da DCL. É a forma certa para "meus pedidos", "minhas tarefas", "meus tickets" (SAP Help — user condition).
define role ZI_MY_ORDERS {
grant select on ZI_SalesOrder
where CreatedBy = aspect user;
}Os operadores válidos são =, <> e ?=. E sim, o mesmo cuidado do ?= vale aqui: ele tolera NULL/inicial, não "libera quem não tem alias". Você pode combinar com NULL para tratar registros "sem dono" como públicos:
define role ZI_OWN_DOCS {
grant select on ZI_DocMode
where ( UserName = aspect user
or UserName is initial
or UserName is null );
}Nuance de sintaxe: aspect user é o equivalente em DCL de $session.user, mas não é a mesma coisa: $session.user é um recurso de DDL e variáveis de sessão não podem ser usadas dentro da DCL. Dentro de uma role, use aspect user.
Como reaproveitar regras com herança (INHERITING CONDITIONS FROM)?
A inheritance condition reutiliza as condições já definidas em outra DCL, evitando duplicação — o pilar da estratégia VDM (Virtual Data Model), onde a Consumption View (ZC_*) herda da Interface View (ZI_*) (SAP Help — inherit_condition).
define role ZC_SALES_ORDER {
grant select on ZC_SalesOrder
where inheriting conditions from entity ZI_SalesOrder;
}Você pode até substituir valores de campos PFCG na herança com REPLACING — por exemplo, transformar autorização de exibição (03) em alteração (02) na view derivada:
define role ZC_SALES_ORDER_CHG {
grant select on ZC_SalesOrderChg
where inheriting conditions from entity ZI_SalesOrder
replacing { pfcg_filter field ACTVT value '03' with '02' };
}Correção sobre o DEFAULT: ao contrário do que circula por aí, nem DEFAULT TRUE nem DEFAULT FALSE é "o padrão". Omitir a cláusula DEFAULT gera erro de sintaxe caso a entidade-fonte não tenha uma role. Defina explicitamente: DEFAULT TRUE = acesso pleno se o pai não tiver DCL; DEFAULT FALSE = bloqueia tudo (mais seguro).
Como combinar várias regras (COMBINATION MODE OR/AND)?
Quando há várias regras para a mesma entidade, o COMBINATION MODE define como elas se combinam. O padrão é OR (regras implicitamente unidas por "ou"), que amplia o acesso; o AND estreita (SAP Help — conditional_rule).
// OR (padrão) — vê linhas se tem PFCG OU é o criador
define role ZI_ORDER_PERMISSIVE {
grant select on ZI_SalesOrder combination mode or
where ( SalesOrderType ) = aspect pfcg_auth( V_VBAK_AAT, AUART );
grant select on ZI_SalesOrder combination mode or
where CreatedBy = aspect user;
}A fórmula resultante quando há regras dos dois modos é:
( cond_or_1 OR cond_or_2 OR ... ) AND cond_and_1 AND cond_and_2 AND ...Atenção: uma full access rule anula a construção acima. Se existir uma regra de acesso pleno para a entidade, o resultado é acesso pleno — mesmo havendo regras com COMBINATION MODE AND. Não misture as duas coisas sem perceber.
Como substituir a DCL entregue pela SAP (REDEFINITION)?
O REDEFINITION permite ao cliente/parceiro substituir toda a DCL entregue pela SAP para uma entidade, sem modificação. É poderoso e arriscado: após a ativação, só a sua regra vale, e melhorias futuras da SAP naquela DCL deixam de ser aplicadas. Ele só pode ser usado uma vez por entidade (mais que isso gera erro de ativação) e apenas em sistemas de cliente.
// Substituição total
define role ZC_REDEFINE_SALES {
grant select on I_SalesDocument
redefinition
where SalesOrganization = '1710';
}Para não perder as condições originais, traga-as de volta com INHERITING CONDITIONS FROM SUPER e acrescente exceções:
define role ZC_REDEFINE_KEEP {
grant select on I_SalesDocument
redefinition
where inheriting conditions from super
or SalesOrganization = '9999';
}Como restringir por associações (ALL, EXISTS e BYPASS WHEN)?
Para condições sobre path expressions (associações), o DCL oferece quantificadores. ALL exige que todos os registros associados atendam; EXISTS exige pelo menos um. São recursos mais recentes — disponíveis a partir do release 7.55 (SAP Help — literal condition).
// Acesso ao projeto só se TODAS as tarefas estão concluídas
where all _ProjectTasks.Status = 'COMPLETED';
// Pelo menos uma tarefa concluída
where exists _ProjectTasks.Status = 'COMPLETED';O BYPASS WHEN exclui do filtro os registros associados com valor nulo/inicial — sem ele, tarefas com status NULL quebrariam a cláusula ALL:
where all ( _ProjectTasks.Status = 'COMPLETED' bypass when is null );Cuidado com performance: ALL/EXISTS sobre associações podem gerar subqueries pesadas. Teste antes de usar em entidades de alto volume — e evite-os em entidades simples sem associação, onde só aumentam a complexidade.
PRIVILEGED ACCESS, roles SWITCHABLE e o modo de emergência
Quando um job técnico precisa de visão consolidada, o WITH PRIVILEGED ACCESS no ABAP SQL ignora a DCL daquela leitura. Use só em código bem auditado — nunca em handler exposto ao usuário final. É também o caminho obrigatório para ler entidades marcadas como #PRIVILEGED_ONLY.
SELECT * FROM zi_salesorder WITH PRIVILEGED ACCESS
INTO TABLE @DATA(lt_orders).Já o SWITCHABLE liga a role ao Switch Framework, útil para funcionalidades opcionais. Atenção às restrições documentadas: numa role switchable o COMBINATION MODE é obrigatório, e REDEFINITION e herança não são permitidos.
@MappingRole: true
define role switchable ZI_OPTIONAL_AUTH {
grant select on ZI_SalesOrder combination mode or
where ( SalesOrganization ) = aspect pfcg_auth( V_VBAK_VKO, VKORG );
}Dois fatos úteis: no modo de emergência (usuário SAP*), o CDS access control é desativado — e isso afeta condições PFCG, literais e de usuário, todas. E o Data Preview do ADT respeita a DCL: para entidades #PRIVILEGED_ONLY, você precisa adicionar WITH PRIVILEGED ACCESS para conseguir visualizar.
Sobre o @MappingRole: para que serve mesmo?
Aqui há outra confusão comum. A annotation @MappingRole: true não é uma exigência específica do aspect pfcg_auth. Seu papel real: marcar a role como uma mapping role, fazendo com que ela seja atribuída/aplicada a todos os usuários, independentemente de cliente. Se você usá-la, o valor deve ser true.
Na prática, é boa prática incluí-la nas suas DCLs — mas pelo motivo certo (aplicar a role a todos), não porque "o PFCG exige".
DCL e RAP: leitura vs. modificação
O DCL protege apenas a leitura. Para create, update, delete e actions, a proteção fica na Behavior Definition do RAP, via Global e Instance Authorization. Os dois são complementares: DCL filtra o que o usuário lê; a Behavior Authorization controla o que ele pode alterar.
define behavior for ZI_SalesOrder alias SalesOrder
persistent table zsalesorder
lock master
authorization master ( instance )
{
create;
update;
delete;
action approveOrder authorization:instance;
action cancelOrder authorization:global;
}Quer o quadro completo da modificação? O DCL é só a camada de leitura. Para entender Global vs. Instance Authorization — e quando usar cada uma — veja o guia Os 3 tipos de autorização no SAP RAP: DCL, Global e Instance.
Boas práticas e armadilhas
Algumas práticas mantêm o DCL seguro e sustentável. A mais importante: defina a DCL ao mesmo tempo que a CDS, não como passo posterior — e concentre a regra "fonte" na camada Interface, deixando a Consumption herdar via INHERITING CONDITIONS FROM ENTITY.
Declare
@AccessControl.authorizationCheckexplicitamente em toda entidade exposta.Use
#MANDATORYem entidades sensíveis (HR, financeiro).Em ABAP Cloud, use só objetos de autorização released (contrato C1).
Combine DCL (leitura) + RAP authorization (modificação) para um BO completo.
Logue cada
WITH PRIVILEGED ACCESSpara auditoria e bloqueieSAP*em produção.
Antipadrão | Risco |
|---|---|
| Dados sensíveis expostos |
| Esquecimento em produção |
Full access rule "temporária" | Permanece e libera dados em prod |
| Perde melhorias SAP em upgrades |
DCL na Consumption sem herdar da Interface | Regras duplicadas que divergem |
Misturar | Full access vence; o AND é ignorado |
Uma observação honesta sobre limites: ao contrário do que alguns blogs afirmam, não existe um "máximo de 4 níveis de AND" oficial na documentação. Estruture as condições pela clareza, não por um limite inventado.
Perguntas frequentes
O DCL substitui o AUTHORITY-CHECK clássico?
Para filtrar linhas na leitura, sim — o DCL é o mecanismo moderno e declarativo. Mas para start authorization (permitir entrar na app) você ainda usa AUTHORITY-CHECK no início do programa, e para modificações usa a autorização do RAP. DCL não cobre tudo; cobre leitura.
Por que minha view com DCL ainda mostra tudo quando consumida por outra?
Porque o controle de acesso só é aplicado no acesso direto à entidade que tem a DCL. Quando outra view faz SELECT FROM a sua, a DCL interna não dispara. A proteção precisa estar na entidade efetivamente consultada — por isso a Consumption View deve herdar via INHERITING CONDITIONS FROM ENTITY.
O que o operador ?= realmente faz?
Ele funciona como o =, mas a condição também é satisfeita quando os elementos CDS à esquerda têm valor NULL ou inicial. É tolerância a nulos — não "libera acesso pleno para quem não tem autorização", como muitas fontes afirmam erroneamente. Vale tanto para PFCG quanto para user condition.
Preciso de @MappingRole: true sempre?
Não é uma exigência do pfcg_auth, como muito material diz. Ele marca a role como mapping role, aplicada a todos os usuários independentemente de cliente; se presente, o valor deve ser true. É boa prática incluí-lo — mas pelo motivo correto, não por causa do PFCG.
Como desligo a DCL para um job técnico?
Use WITH PRIVILEGED ACCESS no SELECT, em código auditado. Em RAP, o equivalente é a leitura IN LOCAL MODE, que também pula o controle de acesso. Nunca deixe esses bypasses em código exposto ao usuário final — eles furam todo o modelo de segurança.
Conclusão
O CDS Access Control transforma controle de acesso por linha de um emaranhado de AUTHORITY-CHECK num modelo declarativo, central e reaproveitável. Escolha a condição certa para cada caso — PFCG para roles existentes, user para "meus registros", literal para valores fixos —, herde entre camadas com INHERITING CONDITIONS e reserve REDEFINITION e PRIVILEGED ACCESS para os casos específicos que os justificam.
E lembre dos dois enganos mais comuns que este guia corrige: o ?= é tolerância a NULL (não override de autorização), e a checagem só vale no acesso direto à entidade. Combine DCL na leitura com a autorização do RAP na modificação, e o seu Business Object fica protegido de ponta a ponta.
Referências oficiais
Comentários 0
Ainda sem comentários
Seja o primeiro a comentar este artigo.
Entre na conversa
Faça login para deixar seu comentário neste artigo.