İçeriğe geç

CDS Yetkilendirme: DCL ile Satır Bazlı Erişim Kontrolü

ABAP SQL veritabanında yetki kontrolü yapmaz; okuduğunuz her satırı kimin görebileceği sizin sorumluluğunuzdadır. CDS access control bu kontrolü programdan alıp veri modelinin yanına koyar. Nasıl kurulduğunu, katmanlı görünümlerde nerede delindiğini ve RAP'ta neyi kapsamadığını anlatıyorum.

Mustafa Önder Mustafa Önder  ·  14 Ekim 2026  ·  16 dk okuma

İçindekiler

  1. Neden Ayrı Bir Yetki Katmanı?
  2. @AccessControl.authorizationCheck
  3. İlk Rol: pfcg_auth ile Şirket Kodu
  4. Koşul Türleri ve ?= Operatörü
  5. Katmanlı Görünümler ve Kalıtım
  6. RAP: Okuma DCL'de, Değiştirme Davranışta
  7. WITH PRIVILEGED ACCESS
  8. DCL'nin Devreye Girmediği Yerler
  9. Rolü Nasıl Sınarım?
  10. Sık Yapılan Hatalar
  11. Sonuç

Klasik ABAP'ta yetki kontrolü bir alışkanlıktır: program başında AUTHORITY-CHECK, gerekirse döngüde her satır için bir kontrol daha. Bu yöntem çalışır, ama kontrol programın içindedir. Aynı tabloyu okuyan ikinci bir program, bir OData servisi ya da bir rapor bu kontrolü unutursa veri açılır.

CDS access control (DCL, Data Control Language) yetkiyi veri modelinin yanına taşır: bir CDS varlığı için bir kez rol tanımlarsınız, o varlık ABAP SQL ile her okunduğunda kullanıcının yetkisine göre süzülür. Bu yazıda bu mekanizmayı, katmanlı görünümlerde ve RAP'ta nasıl kullanılacağını ve en önemlisi nerede devreye girmediğini anlatıyorum. Sözdizimi ve davranış SAP'nin ABAP anahtar kelime belgesine ve resmi ABAP cheat sheet deposuna dayanır; ayrıntılar sürüme göre değişebildiği için kendi sisteminizin belgesini de kontrol edin.

Neden Ayrı Bir Yetki Katmanı?

ABAP SQL ifadeleri veritabanında yetki kontrolü tetiklemez; bir SELECT, programı çalıştıran kullanıcı kim olursa olsun tablodaki bütün satırlara erişebilir. Bu yüzden yetki iki yoldan kontrol edilir:

AUTHORITY-CHECK ile CDS access control karşılaştırması
KonuAUTHORITY-CHECK (açık)CDS access control (örtük)
Nerede yazılır?Programın içinde, her kullanım yerindeCDS varlığı için bir kez, ayrı bir DCL nesnesinde
Ne zaman çalışır?Geliştirici çağırdığındaVarlık ABAP SQL ile her okunduğunda
Sonuçsy-subrc; karar programa kalırYetkisiz satırlar sonuçta yer almaz; hata dönmez
KapsamHer işlem (oluşturma, değiştirme, silme, aksiyon)Yalnız okuma

Son satır önemli: DCL bir okuma filtresidir. Kullanıcının bir kaydı değiştirip değiştiremeyeceğini söylemez. Bu yüzden iki yöntem birbirinin yerine geçmez, birlikte kullanılır.

Davranış farkını kullanıcıya anlatın: DCL yetkisiz satırları sessizce süzer. Yetkisi eksik bir kullanıcı "yetkiniz yok" mesajı görmez, boş ya da eksik bir liste görür. Destek ekibinin "kayıt yok" şikâyetinde önce yetkiye bakması gerektiğini bilmesi gerekir.

@AccessControl.authorizationCheck

Her CDS varlığı, kendisi için bir rol beklenip beklenmediğini bu anotasyonla söyler:

@AccessControl.authorizationCheck değerleri ve anlamları
DeğerAnlamı
#CHECKRol beklenir; yoksa etkinleştirmede uyarı verilir. Rol varsa uygulanır.
#MANDATORYRol zorunludur; varlık için bir DCL nesnesi bulunmalıdır.
#NOT_REQUIREDErişim kontrolünün gerekmediğini söyler; rol yoksa uyarı verilmez.
#NOT_ALLOWEDRol tanımlanmamalıdır; tanımlansa bile dikkate alınmaz.

Benim önerim: kullanıcıya veya dış sisteme açılan her varlıkta #CHECK (ya da kuralı sertleştirmek için #MANDATORY) kullanın ve uyarıyı yok saymayın. #NOT_REQUIRED'ı ülke, birim, durum metni gibi gerçekten herkese açık veriler için saklayın. Bir varlıkta #NOT_REQUIRED görmek, kod incelemesinde "bu veri neden herkese açık?" sorusunu sordurmalıdır.

İlk Rol: pfcg_auth ile Şirket Kodu

Örnek olarak bir masraf formu tablosu düşünelim: her formun bir şirket kodu ve masraf yeri var. Kullanıcı yalnız yetkili olduğu şirket kodlarındaki formları görmeli. Yetki, şirket kodu (BUKRS) ve faaliyet (ACTVT) alanlarından oluşan kendi yetki nesnemizde tutuluyor: ZEXP_BUKRS.

Adım 1 — Rol bekleyen görünüm

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Masraf formu'
define view entity ZI_ExpenseReport
  as select from zexp_report
{
  key report_uuid  as ReportUUID,
      report_id    as ReportID,
      company_code as CompanyCode,
      cost_center  as CostCenter,
      created_by   as CreatedBy,
      status       as Status,
      @Semantics.amount.currencyCode: 'Currency'
      total_amount as TotalAmount,
      currency     as Currency
}

Etkinleştirdiğinizde, henüz rol olmadığı için bir uyarı görürsünüz. Bu uyarı tam istediğimiz şeydir: varlık rol beklediğini söylüyor.

Adım 2 — DCL nesnesi (rol)

@EndUserText.label: 'Masraf formu: şirket kodu yetkisi'
@MappingRole: true
define role ZI_ExpenseReport {
  grant select on ZI_ExpenseReport
    where ( CompanyCode ) = aspect pfcg_auth( ZEXP_BUKRS, BUKRS, ACTVT = '03' );
}

Bu rolün söylediği şey: "ZI_ExpenseReport okunurken yalnız, kullanıcının ZEXP_BUKRS nesnesinde ACTVT = '03' (görüntüleme) içeren yetkilerindeki BUKRS değerlerine uyan satırları getir." Rol etkinleştikten sonra görünümdeki uyarı kaybolur.

Belgede tanımlanan birkaç kural, rolün davranışını anlamak için önemlidir:

  • Eşleme sırayla yapılır. Soldaki eleman sayısı, sağdaki yetki alanı sayısıyla aynı olmalıdır: ( CompanyCode, CostCenter ) = aspect pfcg_auth( ZEXP_AUTH, BUKRS, KOSTL, ACTVT = '03' ).
  • Sabit değerler süzgeçtir. ACTVT = '03' yazdığınızda yalnız bu değeri içeren yetkiler hesaba katılır.
  • Birden çok yetki "veya" ile birleşir. Kullanıcının iki ayrı yetkisi varsa (biri 1000, öteki 2000 şirket kodu için), iki koşul OR ile bağlanır.
  • Tam yetki koşul üretmez. Bir alanda tam yetki (*) varsa o alan için koşul oluşmaz; bu durumda boş (null) değerler de kabul edilir.
  • Yetki hiç yoksa koşul sağlanmaz. Kullanıcı hiçbir satır görmez.

On-premise sistemlerde yetki nesnesi PFCG rolleriyle kullanıcıya verilir. ABAP Cloud'da (BTP ABAP ortamı, S/4HANA Cloud) aynı yetki nesnesi bir IAM uygulamasına, oradan iş kataloğuna ve iş rolüne bağlanır. DCL tarafındaki rol iki dünyada da aynıdır; değişen yalnız yetkinin kullanıcıya nasıl ulaştığıdır.

Koşul Türleri ve ?= Operatörü

Bir erişim koşulu yalnız pfcg_auth'tan oluşmak zorunda değildir. Koşullar and / or ile birleştirilebilir:

  • Literal koşul: bir elemanı sabit bir değerle karşılaştırır (Status <> 'X').
  • PFCG koşulu: yukarıdaki aspect pfcg_auth.
  • Kullanıcı koşulu: elemanı oturum açmış kullanıcının adıyla karşılaştırır (CreatedBy = aspect user). İzin verilen operatörler =, <> ve ?='dir.
  • Kalıtım koşulu: başka bir varlığın koşullarını devralır (aşağıda).
  • true / false: her zaman sağlanan ya da hiç sağlanmayan koşul.

Örneğin "şirket kodu yetkisi olan herkes onaylanmış formları görsün, kendi formunu ise her durumda görsün" kuralı şöyle yazılabilir:

@EndUserText.label: 'Masraf formu: şirket kodu veya kendi formu'
@MappingRole: true
define role ZI_ExpenseReport {
  grant select on ZI_ExpenseReport
    where ( ( CompanyCode ) = aspect pfcg_auth( ZEXP_BUKRS, BUKRS, ACTVT = '03' )
            and Status = 'A' )
       or CreatedBy = aspect user;
}

?= operatörü: bilerek kullanın

= yerine ?= yazıldığında koşul, soldaki bütün elemanlar null olduğunda da sağlanır, ve belgeye göre bu, kullanıcının hiç yetkisi olmasa bile geçerlidir. İsteğe bağlı bir ilişkiden (ör. henüz atanmamış bir masraf yeri) gelen alanlarda satırın tamamen kaybolmaması için tasarlanmıştır. Ama yan etkisi açıktır: alanı boş olan satırlar herkese görünür. ?='yi bir alışkanlık olarak değil, "bu alan boşsa satır herkese açık olabilir" kararını verdikten sonra kullanın.

Katmanlı Görünümler ve Kalıtım

DCL'yi yeni kullananların en sık atladığı kural şudur: yalnız ABAP SQL'de doğrudan okunan varlığın rolü değerlendirilir. Bir görünüm başka bir görünümden okuyorsa, alttaki görünümün rolü otomatik olarak uygulanmaz.

Tipik bir RAP veya Fiori yığınında bu şu anlama gelir: ZI_ExpenseReport için rol yazdınız, ama Fiori uygulaması ZC_ExpenseReport projeksiyonunu okuyor. Projeksiyonun kendi rolü yoksa, alttaki rol okunan veriyi süzmez. Her katmana aynı koşulu kopyalamak yerine kalıtım kullanın:

@EndUserText.label: 'Masraf formu projeksiyonu'
@MappingRole: true
define role ZC_ExpenseReport {
  grant select on ZC_ExpenseReport
    where inheriting conditions from entity ZI_ExpenseReport;
}

Kalıtım, alttaki varlığın koşullarını bu varlığa uygular; alt rol değişince üst katman da değişir. Belgede yer alan birkaç ayrıntı:

  • Devralınan varlığın henüz hiç koşulu yoksa default true ya da default false eklemek zorunludur; yoksa sözdizimi hatası alırsınız.
  • replacing { ... } ile devralınan koşulda değişiklik yapılabilir. Örneğin pfcg_filter field ACTVT value '03' with '02' devralınan görüntüleme koşulunu değiştirme faaliyetine çevirir; alan adları farklıysa element ... with ... (yalnız transactional projection görünümlerinde) ya da ilişki üzerinden root with ... kullanılır.
  • Kalıtımın başına not konamaz ve döngüsel kalıtım sözdizimi hatasıdır.
  • Üst varlıktaki @AccessControl.authorizationCheck anotasyonu kalıtımda dikkate alınmaz; koşullar yine devralınır.

Alt varlıklar: yol ifadesiyle koşul

Kalem tablosunda şirket kodu yoktur; kalem, başlığın şirket koduna göre görünmelidir. PFCG koşulunun solunda eleman yerine yol ifadesi yazılabilir:

@EndUserText.label: 'Masraf kalemi: başlığın şirket kodu'
@MappingRole: true
define role ZI_ExpenseItem {
  grant select on ZI_ExpenseItem
    where ( _Report.CompanyCode ) = aspect pfcg_auth( ZEXP_BUKRS, BUKRS, ACTVT = '03' );
}

Kalem ve başlık için ayrı yetki mantığı kurmak yerine kalemi başlığa bağlamak, iki listenin birbirinden farklı sonuç göstermesini önler.

RAP: Okuma DCL'de, Değiştirme Davranışta

RAP'ta okuma işlemleri için CDS access control kullanılır ve managed senaryolarda otomatik uygulanır. Ama oluşturma, değiştirme, silme ve aksiyonlar için DCL hiçbir şey söylemez. Bunlar behavior definition'daki authorization master ile tanımlanır ve behavior pool'da AUTHORITY-CHECK ile uygulanır:

  • Global yetki (authorization master ( global )): kaydın durumundan bağımsız soru. "Bu kullanıcı hiç form silebilir mi?" Yetki yoksa istek, silme metoduna hiç ulaşmaz.
  • Instance yetkisi (authorization master ( instance )): kaydın içeriğine bağlı soru. "Bu kullanıcı 1000 şirket kodundaki bu formu değiştirebilir mi?"
  • İkisi birlikte tanımlanabilir. Draft senaryolarında Edit aksiyonu güncelleme gibi ele alınır; onu da kontrol edin.
METHOD get_global_authorizations.

  IF requested_authorizations-%delete = if_abap_behv=>mk-on.
    AUTHORITY-CHECK OBJECT 'ZEXP_BUKRS'
      ID 'BUKRS' DUMMY
      ID 'ACTVT' FIELD '06'.

    result-%delete = COND #( WHEN sy-subrc = 0
                             THEN if_abap_behv=>auth-allowed
                             ELSE if_abap_behv=>auth-unauthorized ).
  ENDIF.

ENDMETHOD.

Burada BUKRS DUMMY bilinçlidir: global yetki kaydın şirket kodunu bilmez, yalnız "bu kullanıcının herhangi bir şirket kodunda silme yetkisi var mı?" diye sorar. Hangi şirket kodunda silebileceği instance yetkisinin işidir. Tam bir draft ve yetki örneği için SAP RAP rehberine ve alt varlık yetkisini de içeren RAP proje örneğine bakabilirsiniz.

Okuma ve yazma aynı yetkiye dayanmalı. DCL'de ACTVT = '03' ile şirket kodu süzüyor, instance yetkisinde ise başka bir yetki nesnesine bakıyorsanız, kullanıcı göremediği bir kaydı değiştirebilir ya da görebildiği bir kaydı açtığında anlaşılmaz bir hata alabilir. Aynı yetki nesnesini, farklı faaliyet değerleriyle kullanın.

WITH PRIVILEGED ACCESS

Bazen bir programın kullanıcı yetkisinden bağımsız olarak bütün veriyi okuması gerekir: bütün şirket kodlarının toplamını hesaplayan bir arka plan işi ya da bir tutarlılık kontrolü gibi. ABAP SQL'de bunun için bir ek vardır:

SELECT CompanyCode, SUM( TotalAmount ) AS Total, Currency
  FROM zi_expensereport WITH PRIVILEGED ACCESS
  GROUP BY CompanyCode, Currency
  INTO TABLE @DATA(totals).

Belgeye göre bu ek, yazıldığı varlık için CDS access control'ü kapatır ve teslim edilen ya da sizin yazdığınız bütün rolleri geçersiz kılar. Sınırları:

  • Yalnız yazıldığı varlığı etkiler; ilişkiler (association) üzerinden ulaşılan varlıklara yayılmaz.
  • Yol ifadesiyle birlikte kullanılamaz.
  • Veritabanı tablolarında ve klasik görünümlerde etkisizdir; zaten orada DCL yoktur.

Bu ekin her kullanımı, yetki sorumluluğunun DCL'den koda geçtiği bir yerdir. Kod incelemesinde WITH PRIVILEGED ACCESS araması yapmak ve her sonuç için "sonuç kime gösteriliyor?" diye sormak iyi bir alışkanlıktır. Ayrıcalıklı okunan veri bir kullanıcı ekranına gidiyorsa, büyük ihtimalle yanlış yerdesiniz.

DCL'nin Devreye Girmediği Yerler

DCL bir CDS varlığına bağlıdır ve ABAP SQL ile o varlık okunduğunda çalışır. Bunun dışındaki her yol, rolü atlar:

  • Tablonun doğrudan okunması. SELECT ... FROM zexp_report DCL'yi tetiklemez; rol tabloya değil görünüme bağlıdır. Uygulama kodunda yetki gerektiren veriyi tablodan değil, rollü görünümden okuyun.
  • Rolü olmayan üst katman. Yukarıda anlatılan kural: alttaki görünümün rolü, üstteki görünüm okunurken uygulanmaz.
  • AMDP ve Native SQL. SQLScript içinde okunan tablo ve görünümler için DCL değerlendirilmez. Bir CDS table function ise ABAP SQL ile okunduğunda kendi rolünü taşıyabilir. Ayrıntı için AMDP yazısına bakabilirsiniz.
  • Tablo görüntüleme araçları. Tabloyu doğrudan gösteren araçlar CDS rolünü değil, kendi yetki kontrollerini uygular.

Rolü Nasıl Sınarım?

  • ADT veri önizlemesi: Görünümde F8 ile açılan önizleme, oturumdaki kullanıcının yetkisiyle süzülmüş veriyi gösterir. Geliştirici kullanıcıların çoğunlukla geniş yetkisi olduğundan, kısıtlı rolü olan bir test kullanıcısıyla da açın.
  • Karşılaştırmalı sayım: Bir test sınıfında aynı sorguyu normal ve WITH PRIVILEGED ACCESS ile çalıştırıp satır sayısını karşılaştırmak, rolün gerçekten süzdüğünü gösterir.
  • Sınır durumları: Hiç yetkisi olmayan kullanıcı, tam yetkili kullanıcı (*), alanı boş satırlar (özellikle ?= kullandıysanız) ve iki ayrı yetkisi olan kullanıcı. Bu dört durumu sınamadan rolü bitmiş saymayın.
  • Bütün katmanlar: Rolü yalnız arayüz görünümünde değil, uygulamanın gerçekten okuduğu projeksiyonda ve servis üzerinden de sınayın.

Sık Yapılan Hatalar

Rolü yalnız alt katmana yazmak

Fiori uygulaması projeksiyonu okur, rol arayüz görünümündedir; veri süzülmeden ekrana gelir. Her okunan katmana ya kendi koşulunu ya da inheriting conditions from entity ekleyin.

DCL'yi değiştirme yetkisi sanmak

DCL yalnız okumayı süzer. Değiştirme, silme ve aksiyonlar için RAP'ta global ve instance yetkisini ayrıca uygulayın.

#CHECK uyarısını yok saymak

"Rol yok" uyarısı, varlığın herkese açık olduğunu söyler. Uyarıyı kapatmak için #NOT_REQUIRED'a geçmek, sorunu gizler.

Uygulama kodunda tabloyu okumak

Rollü bir görünüm varken aynı veriyi tablodan okuyan bir yardımcı metot, rolü sessizce atlatır. Yetkili veriye tek kapıdan girin.

?='yi varsayılan yapmak

Alanı boş olan her satır herkese açılır. Yalnız boş değerin "herkes görebilir" anlamına geldiği alanlarda kullanın.

WITH PRIVILEGED ACCESS ile kullanıcıya veri göstermek

Bu ek teknik süreçler içindir. Sonucu bir kullanıcıya gidiyorsa, yetki kontrolünü kendiniz yapmadıkça veri sızdırıyorsunuz demektir.

Sonuç

CDS access control, yetkiyi "her programcı hatırlasın" sorumluluğundan çıkarıp veri modelinin bir parçası yapar. Ama yalnız kurulduğu yolda çalışır: ABAP SQL ile okunan, rolü olan varlıkta. Her okunan katmana rol (çoğu zaman kalıtım), değiştirme için RAP yetkisi, tablolara doğrudan erişimin ve ayrıcalıklı okumanın bilinçli olarak sınırlanması, bu modeli gerçekten güvenli yapan üç tamamlayıcıdır.

Clean core ve ABAP Cloud yaklaşımında yetkinin bu şekilde modellenmesi için ABAP Cloud ve clean core yazısına, sunduğum geliştirme kapsamı için SAP geliştirme hizmet sayfasına bakabilirsiniz.

CDS görünümlerinizin yetki modelini gözden geçirmek, DCL rollerini kurmak veya RAP uygulamanızda okuma ve yazma yetkisini birbirine bağlamak için destek alın.

Ön Görüşme Talep Et
← Blog'a Dön

İletişim

Projeleriniz için iletişime geçin.