Yetki sistemi
CPalius'ta "yönetici mi?" diye sorulmaz. Sorulan şey şudur: bu kullanıcı bu eylemi yapabilir mi? Eylemler dize olarak adlandırılır (node.post.edit.own, media.upload, system.update.manage) ve bu dizelere yetenek (capability) denir. Uygulama kodunda ROLE_ADMIN kontrolü yoktur.
Yetenek adları
Ad, alan.nesne.eylem[.kapsam] biçimindedir:
system.update.manage sistemi güncelle
node.post.view.own yalnız kendi yazılarını gör
node.post.edit.any herkesin yazısını düzenle
media.upload medya yükle
forum.post.report forum iletisini şikâyet et
text_format.full_html.use "tam HTML" metin biçimini kullan
Kapsam: .own ve .any
Sahibi olan kayıtlarda iki kapsam vardır:
.own: yalnızca kendi kaydın üzerinde geçerli.any: herhangi bir kayıt üzerinde geçerli
Kapsamsız bir yetenek (ör. node.post.publish) kayıttan bağımsız geçerlidir.
Yetenekler nereden gelir?
Bir yetenek kayıt defterinde (CapabilityRegistry) yoksa geçersizdir ve reddedilir. Böylece yazım hatası ("node.post.edt") sessizce yetki vermez, sessizce reddeder.
Kaynaklar:
- Çekirdek:
cp-core/config/capabilities.yaml.system.*(modül, güncelleme, güvenlik, yedek, cron, kuyruk, API, webhook, alanlar, kullanıcılar, yerelleştirme…),taxonomy.manage,content.*,account.*,core.*,text_format.*.use,admin.*. - Modüller: her modülün
Resources/config/capabilities.yamldosyası. - Kaynaklar:
#[CpResource(capabilities: ['create', 'edit', ...])]kısaltmalarıvehicle.creategibi tam adlara açılır; kodda elle yazmanız gerekmez. - Sözlükler: taksonomi sözlükleri kendi yeteneklerini otomatik kaydeder.
cp:debug komutu, kayıt defterinin bulduğu tüm yetenekleri listeler.
Roller
Roller veritabanında değil, cp-content/config/sync/user.role.<id>.yaml dosyalarındadır. Sebep: roller de kod gibi sürümlenmeli, gözden geçirilmeli ve ortamlar arasında Git ile taşınmalıdır.
role:
id: editor
label: 'role.label.editor'
capabilities:
- admin.dashboard.view
- node.post.view.own
- node.post.create
- node.post.edit.own
- media.upload
- text_format.basic_html.use
Çekirdekle gelen roller:
| Rol | Yetenekler |
|---|---|
admin |
'*' (joker): kayıt defterindeki tüm yetenekler. Kaldırılan ya da tanımsız bir yetenek otomatik düşer |
editor |
Studio panosu, kendi yazı ve sayfaları (oluştur, düzenle, sil, yayınla), yorum moderasyonu, medya, forum etkileşimi, metin biçimleri |
member |
Kayıtlı üye: forum etkileşimi, profil, avatar, kısıtlı metin biçimi. İçerik yönetimi vermez, kayıt olmak içerik yetkisi kazandırmamalıdır |
Bozuk bir rol dosyası (geçersiz YAML, eksik id, eksik capabilities) o rolü atlar; asla fazladan yetki vermez. Ayrıştırılan harita önbelleğe alınır ve dosya değişim zamanıyla geçersiz kılınır.
forum.post.edit gibi kapsamsız bir yetenek, oylayıcı tarafından genel (her kayda) sayılır. Bu yüzden member rolüne yalnızca forum.post.edit.own verilmiştir. Modülünüzde kapsamlı varyantlar tanımlıyorsanız rollere hep kapsamlı olanı verin.
Karar akışı
Her yetki sorusu tek bir kapıdan geçer: is_granted('capability', $konu). Oylayıcı (CPaliusVoter) şu sırayla karar verir:
- Kayıt defteri: yetenek tanımsızsa çekimser (hiçbir zaman izin değil).
- Kullanıcı istisnası: reddet. Kullanıcıya özel
denyher şeyden önce gelir ve kazanır. - Rol yetenekleri: kullanıcının rollerinden biri yeteneği içeriyor mu?
- Kullanıcı istisnası: ver. Kullanıcıya özel
grant. .owndenetimi: kayıtOwnableInterfaceile sahibini söylüyorsa, sahibi bu kullanıcı mı?- Kayıt bazlı izin: bu tek kayıt için kullanıcıya, role ya da herkese verilmiş bir izin var mı?
- Erişim olayı: modüller bir hook ile kararı etkileyebilir (sınırlı).
Kullanıcıya özel istisna
Bir kullanıcıya rolünden bağımsız olarak tek bir yetenek verilebilir ya da alınabilir (UserCapabilityOverride, grant / deny). Ayrıcalık yükselmesini önlemek için bu istisnalar bir politika katmanından geçer.
Kayıt bazlı izin (row-level)
"Bu kullanıcı / bu rol / herkes, şu tek kayıt üzerinde şu yeteneği kullanabilir." (EntityAccessManager). Örneğin bir grup üyeliği ya da paylaşılan bir davetiye bir kaydın görünürlüğünü değiştirdiğinde modül izni o anda yazar. Toplu bir "yeniden kur" adımı yoktur: her izin satırı yazıldığı anda doğrudur.
$access->grantToUser('album', $album->getId(), 'album.view', $userId, $grantedBy);
$access->grantToRole('album', $album->getId(), 'album.view', 'member');
$access->grantToAnyone('album', $album->getId(), 'album.view');
$access->revokeAllForEntity('album', $album->getId());
Listelerde: yetki SQL'e çevrilir
100 satırlık bir listede her satır için oylayıcı çalıştırmak, ciddi bir N+1 sorunu yaratır. CPalius bunu QueryScopeApplier ile çözer: kullanıcının yetenekleri (.any, .own, kayıt izinleri) doğrudan sorgunun WHERE koşuluna eklenir.
$qb = $repo->createQueryBuilder('p');
$scope->apply($qb, 'p', 'node.post.view', 'author');
// .any varsa filtre yok; yalnız .own varsa AND p.author = :me
// kayıt izni varsa OR EXISTS(...)
// hiçbiri yoksa 1 = 0 (boş sonuç)
Kural: yetki yoksa sonuç boş gelir, asla filtresiz tablo gelmez.
Metin biçimi yetenekleri
Zengin metin alanı hangi süzgeçlerden geçeceğini bir metin biçimi ile söyler. Kullanıcının hangi biçimleri seçebileceği yetenekle belirlenir: text_format.restricted.use, text_format.basic_html.use, text_format.full_html.use, text_format.markdown.use. Üye yalnızca kısıtlı biçimi kullanır; editör tam HTML'e kadar çıkar.
Çok kiracılı (SaaS) kullanım
#[CpResource(multiTenant: true)] işaretli kaynaklarda bir Doctrine SQL filtresi tüm SELECT'lere AND tenant_id = :aktif_kiracı ekler, yazmalarda kiracı otomatik damgalanır. Geliştirici disiplinine güvenilmez, yalıtım varsayılandır.
Modül yazarı için
# Resources/config/capabilities.yaml
capabilities:
- mymodule.item.view.own
- mymodule.item.view.any
- mymodule.item.create
- mymodule.item.edit.own
- mymodule.item.edit.any
- mymodule.settings.manage
#[Route('/admin/items/{id}/edit', name: 'admin_item_edit')]
#[IsGranted('mymodule.item.edit')] // kapsamsız yazılırsa .own/.any'den biri yeterli sayılır
public function edit(Item $item): Response { /* ... */ }
- Menüde:
#[CpAdminMenu(capability: 'mymodule.item.view.own|mymodule.item.view.any')]:|ile "veya". - Yeni yetenekleri rollere eklemek için modül kurulumcusu
user.role.*.yamldosyalarına yazabilir (Portal ve Vitrin bunu yapar). cp:doctorrollerde tanımsız bir yetenek kalmışsa uyarır.