CDS Views
RAP / CDS / ODataintermediate

CDS Views Part 4: Authorization with DCL and Clean-Core Data Access

Key takeaway

Control which rows each user may see with CDS access control (DCL): authorizationCheck values, pfcg auth conditions, inheriting roles in projections, privileged access — and read SAP data the clean-core way through released CDS views.

Explanation

📌 KEY POINTS
✅ A DCL role adds a row filter to every read of the view.

✅ #CHECK without a role gives only a warning — data stays open.

✅ Projections need their own role: "inheriting conditions from entity".

⚠️ In clean core, read released views (I_…), never SAP tables.

▶ WHERE WE ARE
Your sales order views return the right data fast (Parts 2–3). But a sales manager in sales organization 1010 must not see orders of 2020. CDS solves this declaratively with access control, and this part also covers how to read SAP data the clean-core way.

━━━━━━━━━━━━━━━━━━━━
▶ 1. HOW CDS ACCESS CONTROL WORKS

  • You write a DCL role (Access Control object) next to the view: "define role ZI_SALESORDER { grant select on ZI_SalesOrder where ... }".
  • The condition refers to view fields and, usually, to a classic authorization object through "aspect pfcg_auth": the user's PFCG role values for that object become a filter.
  • When anyone reads the view with ABAP SQL, through RAP/OData or analytics, the database query automatically gets the extra WHERE condition. The user simply sees fewer rows — no error, no code in the app.

This is row-level security defined once, applied to every consumer.\n\n━━━━━━━━━━━━━━━━━━━━\n▶ 2. THE @AccessControl.authorizationCheck ANNOTATION\nOn the view you state whether access control is expected:

  • #CHECK — a role should exist; if none exists, a syntax warning appears and the view is readable without restriction. Use this for views with business data.
  • #MANDATORY — a role must exist; reading fails if none is defined.
  • #NOT_REQUIRED — no role needed (e.g. value helps with public data, currencies, units).
  • #NOT_ALLOWED — access control must not be used; roles are ignored.
  • #PRIVILEGED_ONLY — only readable with privileged access.

The annotation documents intent; the DCL role does the filtering.

━━━━━━━━━━━━━━━━━━━━
▶ 3. WRITING THE CONDITION

  • pfcg_auth( object, field, ACTVT = '03' ): the view field is compared with the user's allowed values for that authorization field, restricted to activity 03 (display).
  • Several fields: "where ( SalesOrganization, DistributionChannel ) = aspect pfcg_auth( ZSO_ORG, VKORG, VTWEG, ACTVT = '03' )".
  • Literal conditions: "and OrderType <> 'ZINT'".
  • Combine several grant statements; a row is visible if any grant allows it.
  • User-based conditions: "aspect user" compares a field with the current user (e.g. only own requests).

━━━━━━━━━━━━━━━━━━━━
▶ 4. PROJECTIONS AND STACKED VIEWS

A consumption or projection view on top of a protected view does not automatically inherit its role. Add a role for the projection that reuses the base conditions:

"grant select on ZC_SalesOrder where inheriting conditions from entity ZI_SalesOrder;"

Then a change to the base role applies to every consumer.

━━━━━━━━━━━━━━━━━━━━
▶ 5. WHAT ACCESS CONTROL DOES NOT COVER

  • Direct SELECTs on the database table bypass it — another reason to read via views.
  • It restricts reading, not changing. Changes are protected by RAP authorization (global and instance authorization in the behavior implementation).
  • Developers can bypass it deliberately with "WITH PRIVILEGED ACCESS" in ABAP SQL, e.g. for a technical job that must see everything. Use it only with a documented reason.
  • An empty list is often correct behavior, not a bug — check the user's roles (SU53, or the ADT access control analysis) before debugging the view.

━━━━━━━━━━━━━━━━━━━━
▶ 6. CLEAN-CORE DATA ACCESS

In ABAP Cloud and clean-core development you do not select SAP tables like VBAK or MARA. You read released CDS views (e.g. I_SalesOrder, I_Product, I_Currency) that carry a release contract (C1 for use in cloud development). Benefits: stable across upgrades, correct semantics, and SAP's own access control is already defined on them. In ADT, check the view's API state in the Properties view before using it. If no released view exists for a need, search the SAP Business Accelerator Hub; if still nothing fits, raise it rather than reading tables.

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. Create authorization object ZSO_ORG with fields VKORG and ACTVT; assign values in a test role.
  2. Set @AccessControl.authorizationCheck: #CHECK on ZI_SalesOrder.
  3. Right-click the view → New → Access Control; write the role from the code example; activate.
  4. Read the view with two users with different VKORG values and compare row counts.
  5. Add the inheriting role for the consumption view ZC_SalesOrder.

━━━━━━━━━━━━━━━━━━━━
▶ WHAT YOU HAVE AFTER PART 4

Row-level security that works for every consumer, and the clean-core rule for reading SAP data. Next topic: CDS Associations in depth.

Code example

ABAP Code
// ===== View with access control expected =====@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Sales Order'define view entity ZI_SalesOrder  as select from zso_head{  key sales_order     as SalesOrder,      sales_org       as SalesOrganization,      distr_channel   as DistributionChannel,      order_type      as OrderType,      created_by      as CreatedBy,      @Semantics.amount.currencyCode: 'CurrencyCode'      net_amount      as NetAmount,      currency_code   as CurrencyCode} // ===== DCL role for ZI_SalesOrder =====@EndUserText.label: 'Access control for sales orders'@MappingRole: truedefine role ZI_SALESORDER {  grant select on ZI_SalesOrder    where ( SalesOrganization, DistributionChannel )            = aspect pfcg_auth( ZSO_ORG, VKORG, VTWEG, ACTVT = '03' )      and OrderType <> 'ZINT';          // internal orders never visible here} // ===== Variant: users see only what they created =====// grant select on ZI_SalesOrder where CreatedBy = aspect user; // ===== Projection role reuses the base conditions =====@EndUserText.label: 'Access control for sales order projection'@MappingRole: truedefine role ZC_SALESORDER {  grant select on ZC_SalesOrder    where inheriting conditions from entity ZI_SalesOrder;} " ===== ABAP: normal read is filtered automatically =====SELECT SalesOrder, NetAmount, CurrencyCode  FROM zi_salesorder  INTO TABLE @DATA(my_visible_orders).      " only rows allowed by DCL " Technical job that must see everything - document why!SELECT SalesOrder, NetAmount, CurrencyCode  FROM zi_salesorder WITH PRIVILEGED ACCESS  INTO TABLE @DATA(all_orders). " ===== Clean core: released SAP view instead of table VBAK =====SELECT SalesOrder, SalesOrganization, TotalNetAmount, TransactionCurrency  FROM i_salesorder  WHERE SalesOrganization = '1010'  INTO TABLE @DATA(orders_1010)  UP TO 50 ROWS." Check the released state (API state) of I_SalesOrder in ADT Properties first.

Real project scenario

An analytics app on sales orders was used by regional managers. During UAT a manager from one region exported data of all regions. The CDS views had @AccessControl.authorizationCheck: #CHECK, but nobody had created the DCL roles — the activation only showed a warning, which was ignored.
The team added a role on the interface view using pfcg_auth with the sales organization, and inheriting roles on every consumption view. They also found one custom report that selected the table directly and therefore ignored all restrictions; it was switched to the view. A final check compared row counts per test user before go-live, and the "#CHECK without role" warning was added to the ATC check variant as a blocking finding.

Common mistakes

  • Setting #CHECK and never creating the DCL role (only a warning appears).
  • Assuming projections inherit the base view's role automatically.
  • Reading tables directly, bypassing all access control.
  • Using WITH PRIVILEGED ACCESS for convenience in user-facing code.
  • Treating an empty result as a bug before checking the user's authorizations.
  • Expecting DCL to protect changes — changes need RAP authorization.
  • Selecting SAP tables (VBAK, MARA) in clean-core code instead of released views.

Best practices

  • Use #CHECK on every view with business data and create the role in the same transport.
  • Base conditions on authorization objects via pfcg_auth, activity 03.
  • Use "inheriting conditions from entity" for projections and consumption views.
  • Test with several restricted users and compare row counts.
  • Document every WITH PRIVILEGED ACCESS usage.
  • Read SAP data through released CDS views and check their API state.

Interview angle

Common questions: "How is row-level authorization implemented in CDS?", "What does @AccessControl.authorizationCheck: #CHECK do?", "Does a projection inherit access control?", "How do you read SAP data in ABAP Cloud?"
Strong answer: a DCL role with grant select and pfcg_auth conditions adds a WHERE filter to every read of the view; the annotation only states the expectation (#CHECK warns if no role exists). Projections need their own role, ideally "inheriting conditions from entity". Access control covers reads only; changes are protected by RAP authorization; WITH PRIVILEGED ACCESS bypasses it deliberately. In clean core you read released CDS views, not tables. Mentioning the "#CHECK without a role" trap shows real project experience.

Practice this lesson

Quick check

1. A view has @AccessControl.authorizationCheck: #CHECK but no DCL role exists. What happens?

2. A view has @AccessControl.authorizationCheck: #CHECK, but users see all company codes. What is the most likely reason?

3. In the @AccessControl.authorizationCheck annotation on a CDS view, what does setting the value to #NOT_REQUIRED indicate?

Interview questions

1. Regional managers can see sales orders of all regions in a CDS-based app although @AccessControl.authorizationCheck: #CHECK is set. What went wrong and how do you fix it?

Answer it in your own words first, then compare.

2. What is a CDS view entity, and how does it differ from a classic CDS view defined with @AbapCatalog.sqlViewName?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics