Ne demek, “Kullanıcı Yönetici olup olmadığına karar vermemelidir. Ayrıcalıklar veya Güvenlik sistemi gerekir. ”


55

Soruda kullanılan örnek , kullanıcının yönetici olup olmadığını belirlemek için en iyi şekilde dokunan bir işleve çıplak veri iletmektedir. Yaygın cevaplardan biri şuydu:

user.isAdmin()

Bu, birkaç kez tekrarlanan ve birçok kez oylanan bir yorum yapılmasına yol açtı:

Bir kullanıcı bir Yönetici olup olmadığına karar vermemelidir. Ayrıcalıklar veya Güvenlik sistemi gerekir. Bir sınıfa sıkıca bağlı olan bir şey, onu sınıfın bir parçası yapmanın iyi bir fikir olduğu anlamına gelmez.

Yanıtladım,

Kullanıcı hiçbir şeye karar vermiyor. Kullanıcı nesnesi / tablosu her kullanıcı hakkında veri depolar. Gerçek kullanıcılar kendileriyle ilgili her şeyi değiştiremezler.

Ancak bu üretken değildi. Açıkça, iletişimi zorlaştıran temel bir perspektif farkı var. Birisi bana neden user.isAdmin () 'in kötü olduğunu anlatabilir ve "doğru" gibi göründüğünün kısa bir taslağını çizebilir mi?

Gerçekten, güvenliği koruduğu sistemden ayırmanın avantajını göremiyorum. Herhangi bir güvenlik metni, güvenliğin başlangıcından itibaren bir sistemde tasarlanması ve geliştirme, dağıtım, bakım ve hatta yaşamın her aşamasında göz önünde bulundurulması gerektiğini söyleyecektir. Yana takılabilen bir şey değildir. Ancak bu yorumda şu ana kadar 17 oy daha var önemli bir şeyi kaçırdığımı söylüyor.


8
Kötü değil, özellikle kullanıcı db tablosundaki bir bayraksa bence. Gelecekte değişiklikler olursa , gelecekte de değiştirin. Güvenlik sistemini kullanıcı nesnesinden de arayabilirsiniz. Asla izin verilmeyen bir şey yapmayı denememek için her db / kaynak erişiminin geçerli kullanıcı nesnesinden geçmesi gereken tasarım modelini bir kez okudum. Biraz fazla olabilir, ancak kullanıcı güvenlikle ilgili tüm şeyleri kapsayan bir "ayrıcalıklar" nesnesi sağlayabilir.
Cephalopod

Bir yanlış anlaşılma olduğunu düşünüyorum. Sizin için, "Kullanıcı" yazılımı kullanan kişi anlamına gelir. Onlar için, "Kullanıcı", fonksiyonelliğinizi kullanan kod anlamına gelir.
Philipp

2
İstediğin gibi değil, ama bu durumda en çok dikkat edeceğim şey, nereye koyduysan bir IsAdmin () yöntemi. Genelde bir "Yönetici" nin kodlanmış olmamasını tavsiye ederim, ancak bir dizi olası izin var ve bir "Yönetici" tam sete sahip oluyor. Bu şekilde, Yönetici rolünü daha sonra bölmeniz gerektiğinde, işler çok daha kolaydır. Ve özel durum mantığını azaltıyor.
psr

1
@Philipp Sanmıyorum ki ... Her iki site de açıkça bir Usernesneyle isAdmin()(sahnelerin arkasında ne yaparsa
yapsın

Düzenlemek için çok geç; "Her iki taraf da" demek istedim, "Her iki site" de değil ... ... = P
Izkata

Yanıtlar:


12

Kullanıcı'yı bir anahtar olarak ve izinleri bir değer olarak düşünün.

Farklı bağlamlar, her kullanıcı için farklı izinlere sahip olabilir. İzinler içeriğe bağlı olduğundan, her bağlam için kullanıcıyı değiştirmeniz gerekir. Bu nedenle, bu mantığı başka bir yere (örneğin bağlam sınıfı) koymanız ve gereken izinlere bakmanız daha iyi.

Bunun bir yan etkisi sisteminizi daha güvenli hale getirmesidir, çünkü ilgili olmayan yerler için yönetici bayrağını temizlemeyi unutamazsınız.


40

Bu yorum kötü bir şekilde ifade edildi.

Mesele, kullanıcı nesnesinin bir yönetici mi, deneme kullanıcısı mı, süper kullanıcı mı olduğu veya sıradan veya dışında herhangi bir şey olup olmadığına "karar vermesi" değil . Açıkçası, bunlardan hiçbirine karar vermiyor, sizin tarafınızdan uygulandığı gibi, genel olarak yazılım sistemi karar veriyor. Nokta "kullanıcı" olup olmadığıdır sınıf gerektiğini temsil onun nesneleri temsil anapara rollerini veya bu olsun başka bir sınıfın bir sorumluluktur.


6
Ah (benim yorumum), ama sen benden çok daha iyi koydun.
Marjan Venema

Rolleri Kullanıcı sınıfında değil, ayrı bir Rol sınıfında modellemek için mi diyorsunuz? LDAP gibi ayrı bir sistem önermiyor musunuz? Rol sınıfının verdiği her karar uygun kullanıcı kaydını sorgulamayı gerektiriyorsa ve başka bilgi gerektirmiyorsa? Hala Kullanıcıdan ayrı mı olmalı ve öyleyse neden?
GlenPeterson

2
@GlenPeterson: mutlaka değil, sadece bu sınıflar <> tabloları ve genellikle ayırt edilebilecek çok fazla sınıf var. Veri odaklı olmak, sınıflar üzerindeki alt listeleri tanımlamaya yol açsa da, bu listeler birçok kez başka bir "defter" (siparişler) veya kullanıcıdan geçen bir sınıf (veya sipariş) üzerinde tanımlanmalıdır. Fiil ve isimlerden daha fazla sorumluluk meselesidir.
Marjan Venema

9
Kullanıcı'yı bir anahtar olarak ve izinleri bir değer olarak düşünün. Farklı bağlamlar, her kullanıcı için farklı izinlere sahip olabilir. İzinler içeriğe bağlı olduğundan, her bağlam için kullanıcıyı değiştirmeniz gerekir. Bu mantığı başka bir yere koymanız daha iyi olur (örneğin bağlam sınıfı).
Wilbert

2
@Wilbert: Bağlam! Bu özellikle aydınlatıcı! Evet, bu izinler için birden fazla bağlamınız varsa, o zaman her şey bana mantıklı geliyor. Genellikle bu izinleri tek bir bağlamda görüyorum ve bunları kullanıcıya yapıştırmak bu durumda en basit çözümdür. Açıkça, ikinci bir bağlamda farklı şekilde uygulanırsa oraya ait değiller. Bunu bir cevap olarak yazarsanız, kabul etmem muhtemeldir. Teşekkür ederim.
GlenPeterson

30

Özgün yorumu, anlatıldığı şekilde söylemeyi seçmezdim, ancak potansiyel olarak meşru bir sorunu tespit ediyor .

Spesifik olarak, ayırma emrinin onaylanma ve yetkilendirme olduğu endişeleridir .

Kimlik doğrulama , giriş yapma ve kimlik alma işlemini ifade eder. Sistemler sizin kim olduğunuzu bilir ve kişiselleştirme, nesne mülkiyeti, vb. Gibi şeylerde kullanılır.

Yetkilendirme atıfta yapmanız izin ne ve bu (genellikle) olan değil tarafından belirlenen Kim . Bunun yerine, adınız veya e-posta adresiniz gibi şeylerle ilgilenmeyen roller veya izinler gibi bazı güvenlik politikaları tarafından belirlenir.

Bu ikisi birbirine dik olarak değişebilir. Örneğin, kimlik doğrulama modelini OpenID / OpenAuth sağlayıcılarını ekleyerek değiştirebilirsiniz. Güvenlik politikasını yeni bir rol ekleyerek veya RBAC'den ABAC'ye değiştirerek değiştirebilirsiniz.

Bunların hepsi bir sınıfa veya soyutlamaya girerse, risk azaltma için en önemli araçlarınızdan biri olan güvenlik kodunuz ironik bir şekilde yüksek riskli olur.

Kimlik doğrulama ve yetkilendirmenin çok sıkı bağlandığı sistemler ile çalıştım. Bir sistemde, her biri bir tür "rol" için iki paralel kullanıcı veritabanı vardı. Görünüşe göre, onu tasarlayan kişi veya ekip, tek bir fiziksel kullanıcının her iki rolde olabileceğini veya birden fazla rolde ortak olan bazı eylemler olabileceğini veya Kullanıcı Kimliği çarpışmalarında sorun olabileceğini düşünmemiştir. Bu kuşkusuz aşırı bir örnek, ancak çalışmak için inanılmaz derecede acı vericiydi.

Microsoft ve Sun / Oracle (Java) , Güvenlik Sorumlusu olarak kimlik doğrulama ve yetkilendirme bilgilerinin toplamını ifade eder . Mükemmel değil, ama oldukça iyi çalışıyor. .NET, örneğin, sahip , hangi kapsüller - Eski bir varlık politikası (yetki) nesne ikincisi bir iken kimlik (doğrulama). Birini diğerinin içine koyma kararını makul bir şekilde sorgulayabilirsiniz , ancak önemli olan, yazdığınız çoğu kodun soyutlamalardan yalnızca biri için olacağıdır ; bu, test edilmesi ve yeniden yapılandırmanın kolay olduğu anlamına gelir.IPrincipalIIdentity

Bir User.IsAdminalanla ilgili yanlış olan bir şey yok ... aynı zamanda bir alan olmadığı süreceUser.Name . Bu, "Kullanıcı" kavramının doğru tanımlanmadığını ve ne yazık ki, güvenlik söz konusu olduğunda kulakların arkasında biraz ıslanan geliştiriciler arasında çok yaygın bir hata olduğunu gösterecektir. Tipik olarak, kimlik ve politika ile paylaşılması gereken tek şey, tesadüfen değil, hem Windows hem de * nix güvenlik modellerinde tam olarak nasıl uygulandığı olan Kullanıcı Kimliğidir.

Hem kimliği hem de politikayı içine alan sarıcı nesneler oluşturmak tamamen kabul edilebilir. Örneğin, mevcut kullanıcının erişmesine izin verilen çeşitli widget'lara veya bağlantılara ek olarak "merhaba" mesajı görüntülemeniz gereken bir gösterge paneli ekranı oluşturulmasını kolaylaştıracaktır. Bu sarmalayıcı yalnızca kimlik ve politika bilgilerini tamamladığı ve kendisine ait olduğunu iddia etmediği sürece. Başka bir deyişle, toplu bir kök olarak sunulmadığı sürece .

Basit bir güvenlik modeli , yeni bir uygulama tasarlarken, YAGNI ve diğer şeylerden ötürü her zaman iyi bir fikir gibi gözükse de, neredeyse her zaman sizi daha sonra ısırmaya başlıyor, çünkü sürpriz sürpriz, yeni özellikler eklendi!

Bu nedenle, sizin için neyin en iyisini biliyorsanız, kimlik doğrulama ve yetkilendirme bilgilerini ayrı tutabilirsiniz. Şu anda "yetkilendirme", "IsAdmin" bayrağı kadar basit olsa bile, kimlik doğrulama bilgileriyle aynı sınıf veya tablonun bir parçası değilse, güvenlik politikanızın ne zaman ve ne zaman yapması gerekiyorsa, yine de daha iyi olursunuz. değiştir, zaten iyi çalışan kimlik doğrulama sistemlerinizde rekonstrüktif cerrahi yapmanız gerekmez.


11
Ah, keşke bunu yazmaya vaktim olsaydı. Sonra tekrar, muhtemelen benim açımdan daha fazla düşünmeden yaptığınız kadar iyi koyamazdım. Bağırsak içgüdülerim bana defalarca ya da böyle gitmemi söylüyor, ama neden - kendime ya da başkasına - çok daha fazla düşünce ve çaba gerektirdiğini açıklıyor. Bu yazı için teşekkürler. Birden fazla artıracaktı, ama SE bana izin vermeyecek ...
Marjan Venema

Bu üzerinde çalıştığım bir projeye şok edici bir şekilde benziyor ve cevabınız bana başka bir bakış açısı verdi. Çok teşekkür ederim!
Brandon

"Bir User.IsAdmin alanında yanlış bir şey yok ... aynı zamanda bir User.Name alanı olmadığı sürece" yazıyorsunuz. Fakat isAdminyöntem nedir? Sadece sorumluluğunu izinleri takip etmek olan bir nesneye devredebilir. İlişkisel bir modelin tasarımında en iyi uygulama (bir kuruluşun hangi alanlara sahip olduğu), bir OO modelinde (bir nesnenin hangi mesajlara cevap verebileceği) en iyi uygulamanın ne olduğu ile çevrilebilir değildir.
KaptajnKold,

@KaptajnKold: Bu konuyu bu forumdaki çeşitli soru-cevap başlıklarında okudum. Bir yöntem varsa o takdirde o önemli değil , doğrudan işlemleri gerçekleştirir veya delegeler bunları, diğer sınıflar buna dayanabilir, çünkü hala bir sorumluluk olarak sayar . Aramanız dışında User.IsAdminhiçbir şey yapmayan tek bir astar olabilir PermissionManager.IsAdmin(this.Id), ancak 30 başka sınıf tarafından referans alındığında değiştiremez veya kaldıramazsınız. Bu yüzden SRP var.
Aarona

Ve @KaptajnKold, ilişkisel modellere gelince, ne elde ettiğinden emin değilim; Bir tarlaya sahip olmak daha da kötü . Sistem, RBAC'a veya daha karmaşık bir şeye dönüşdükten ve uzun süre “gerçek” izinlerle senkronize edildikten ve insanlar artık kullanmaları gerekmediğini unuturlar. ve ... peki, sadece hayır de. Yalnızca "admin" ve "admin olmayan" olmak üzere iki rolünüz olduğunu düşünseniz bile, onu bir boolean / bit alanı olarak saklamayın, doğru şekilde normalleştirmeyin ve izin tablosuna sahip olmayın. IsAdmin
Aarona

28

En azından tek sorumluluk ilkesini ihlal ediyor . Değişiklikler olmalı mı (birden fazla yönetici seviyesi, hatta daha farklı roller?) Bu tür bir tasarım oldukça dağınık ve sürdürülmesi berbat olurdu.

Her ikisinin de tek ve iyi tanımlanmış bir rolü olabilmesi için güvenlik sistemini kullanıcı sınıfından ayırmanın avantajını düşünün. Genel olarak tasarımı korumak için daha temiz, daha kolay bir iş çıkar.


2
@GlenPeterson: Hayır, kullanıcılar güvenlik olmadan çok iyi olabilirler. Peki ya benim adım, ekran adım, adres, e-posta adresim vb. Bunları nereye koyacaksınız? Kimlik belirleme (kimlik doğrulama) ve yetkilendirme tamamen farklı hayvanlardır.
Marjan Venema

2
class UserKimlik, Kimlik Doğrulama, Yetkilendirme güvenlik üçlüsünün temel bir bileşenidir . Bunlar tasarım düzeyinde yakından ilişkilidir, bu nedenle kodun bu bağımlılığı yansıtması mantıklı değildir.
MSalters,

1
Sorun şu ki, Kullanıcı sınıfı üçlüyü ele almak için tüm mantığı içermeli midir?
Zavior

3
@ MSalters: eğer böyle bir mantığın API'sı ise, başka bir yerde tam mantığa yetki verse bile bunu biliyor. Gerçekleştirilen nokta, Yetkilendirme'nin (izinlerin) bir kullanıcının başka bir şeyle birleşmesiyle ilgili olmasıdır ve bu nedenle kullanıcının veya başka bir şeyin parçası olmamalıdır, ancak hem kullanıcıların hem de kullanıcıların haklı bir şekilde farkında olan izin sistemlerinin bir parçası olmalıdır. için izin veriliyor.
Marjan Venema

2
Should there be changes- değişiklik olacağını biliyoruz. YAGNI ve hepsi. Gerekli olduğunda bunlar değişiyor mu?
Izkata

10

Bence, belki de yetersiz ifade edilen meselenin Usersınıfa çok fazla ağırlık vermesi olduğunu düşünüyorum . Hayır, User.isAdmin()bu yorumlarda önerildiği gibi bir yönteme sahip bir güvenlik sorunu yoktur . Sonuçta, birisi temel sınıflarınızdan birine kötü niyetli kodlar sokmak için kodunuzda yeterince derinse, ciddi sorunlarınız var. Öte yandan, Userher bağlam için yönetici olup olmadığına karar vermek mantıklı değildir . Farklı roller eklediğinizi düşünün: forumunuz için moderatörler, ön sayfada yayınlar yayınlayan editörler, editörlerin yayınlaması için içerik yazabilen yazarlar vb. UserNesneye koyma yaklaşımıyla User, sınıfla doğrudan ilgili olmayan çok fazla yöntem ve üzerinde yaşayan bir çok mantıkla karşılaşacaksınız .

Bunun yerine, farklı türde izinleri ve bir PermissionsManagersınıfı kullanabilirsiniz. Belki de PermissionsManager.userHasPermission(User, Permission)bir boole değeri döndürmek gibi bir yönteme sahip olabilirsin . Uygulamada, (java'da) gibi görünebilir:

if (!PermissionsManager.userHasPermission(user, Permissions.EDITOR)) {
  Site.renderSecurityRejectionPage();
}

Esasen before_filter, gerçek dünyada bu tür izin denetimlerinin bir örneğini sunan Rails yöntemi yöntemine eşdeğerdir .


6
user.hasPermission(Permissions.EDITOR)OO yerine daha ayrıntılı ve usule dayalı olması dışında bunun farkı nedir?
Cephalopod

9
@Arian: Çünkü user.hasPermissionskullanıcı sınıfına çok fazla sorumluluk yüklüyor. Bir kullanıcının (sınıf) böyle bir bilgiye sahip olmadan var olması gerekirken, kullanıcının izinleri bilmesini gerektirir. Bir kullanıcı sınıfının kendisini nasıl yazdıracağını veya görüntüleyeceğini (form oluşturarak) bilmesini istemezsiniz, bunu bir yazıcı oluşturucu / oluşturucuya veya ekran oluşturucu / oluşturucu sınıfına bırakırsınız.
Marjan Venema

4
user.hasPermission(P)doğru API, ancak bu sadece arayüz. Elbette asıl karar bir güvenlik alt sisteminde verilmelidir. Syrion'un test konusundaki yorumunu ele almak için, test sırasında bu alt sistemi değiştirebilirsiniz. Ancak, anlaşılır user.hasPermission(P)olanın yararı, tüm kodları test etmemeniz için hiçbir şekilde kirletmemenizdir class User.
MSalters

3
@MarjanVenema Bu mantıkla, Kullanıcı hiçbir sorumluluğu olmayan bir veri nesnesi olarak kalacaktır. Böylesi karmaşık bir sisteme ihtiyaç varsa tüm izin yönetiminin kullanıcı sınıfında uygulanması gerektiğini söylemiyorum . Bir kullanıcının izinleri bilmesi gerekmediğini söylüyorsunuz, ancak diğer tüm sınıfların neden bir kullanıcının izinlerini nasıl çözeceğini bilmesi gerektiğini anlamıyorum. Bu, alay etmeyi ve test etmeyi çok daha zorlaştırır.
Cephalopod

6
@Arian: Anemik sınıflar bir koku iken, bazı sınıfların hiçbir davranışı yoktur ... Kullanıcı, imho, bir sürü başka şey için parametre olarak daha iyi kullanılan bir sınıftır (bir şeyler yapmak isteyen) • Kimin ne istediğine dayanarak kararlar verin), her türlü davranış için bir kapsayıcı olarak kullanılmasından ziyade, onu böyle kodlamak uygun görünüyor.
Marjan Venema

9

Object.isX (), nesne ile X arasında, bir boole sonucu olarak ifade edilebilecek açık bir ilişkiyi göstermek için kullanılır, örneğin:

Water.isBoiling ()

Circuit.isOpen ()

User.isAdmin () , sistemin her bağlamında, bir kullanıcının sistemin her bölümünde bir yönetici olduğunu tek bir 'Yönetici' anlamı olduğunu varsayar .

Yeterince basit gibi görünse de, gerçek bir dünya programı bu modele neredeyse hiç uymayacak, kesinlikle bir kullanıcının [X] kaynağını yönetmesi ancak [Y] değil, daha farklı yönetici türleri için bir gereksinim olacaktır. ] yönetici vs [sistem] yönetici).

Bu durum genellikle ihtiyaçların araştırılmasını gerektirir. Nadiren, eğer bir müşteri, User.isAdmin () in gerçekten temsil ettiği ilişkiyi ister, bu yüzden herhangi bir çözümü açıklığa kavuşturmaktan çekinmeyeceğim.


6

Poster sadece farklı ve daha karmaşık bir tasarıma sahipti. Bence User.isAdmin()iyi . Daha sonra bazı karmaşık izinler modelini tanıtsanız bile, taşınması için çok az sebep görüyorum User.isAdmin(). Daha sonra, Kullanıcı sınıfını giriş yapmış bir kullanıcıyı temsil eden bir nesneye ve kullanıcı hakkındaki verileri temsil eden statik bir nesneye bölmek isteyebilirsiniz. Ya da olmayabilir. Yarın için yarını kurtar.


4
Save tomorrow for tomorrow. Evet. Yazdığın sıkı bir şekilde bağlanmış olan kodun seni güldürdüğünü ve tekrar yazman gerektiğine karar verdiğinde, onu ayırmak için fazladan 20 dakika ayırmadın ...
MirroredFate

5
@MirroredFate: User.isAdminKullanıcı sınıfını bu belirli mekanizma ile eşleştirmeden keyfi bir izin mekanizmasına bağlı bir yönteme sahip olmak tamamen mümkündür .
kevin cl

3
User.isAdmin'in keyfi bir izin mekanizmasına, belirli bir mekanizmaya bağlanmadan bile bağlanması için ... iyi ... birleştirme gerekir. Asgari bir arayüze olacaktır. Ve bu arayüz basitçe orada olmamalı. Kullanıcı sınıfına (ve arayüzüne) sorumlu olmaması gereken bir şey veriyor. Aaronaught bunu benden çok daha iyi açıklıyor (bu soruya verilen diğer cevapların bir kombinasyonu gibi).
Marjan Venema

8
@MirroredFate: Daha karmaşık gereksinimleri karşılamak için basit bir uygulamayı yeniden yazmak zorunda kalmamın önemi yok. Ancak, asla ortaya çıkmayan hayal edilen gelecekteki gereksinimleri karşılamak için tasarlanmış aşırı karmaşık API'ler ile gerçekten kötü deneyimler yaşadım.
kevin cl

3
@kevincline Aşırı karmaşık API'ler (tanım gereği) kötü bir şey olsa da, birinin aşırı karmaşık API'ler yazması gerektiğini önermiyordum. Bunun Save tomorrow for tomorrowkorkunç bir tasarım yaklaşımı olduğunu belirttim , çünkü sistem çok daha doğru bir şekilde uygulandıysa önemsiz olabilecek problemleri ortaya çıkardı. Aslında, bu zihniyet, aşırı karmaşık API'lerle sonuçlanan şeydir, çünkü ilk zayıf tasarımı yamalamak için katman üstündeki katman eklenir. Benim duruşum sanırım measure twice, cut once.
MirroredFate

5

Gerçekten bir sorun değil ...

Ben bir problem görmüyorum User.isAdmin(). Kesinlikle PermissionManager.userHasPermission(user, permissions.ADMIN), SRP'nin kutsal adına kodu daha az net hale getiren ve değerli bir şey eklemeyen bir şeye tercih ediyorum .

Bence SRP, bazıları için kelimenin tam anlamıyla biraz yorumlanıyor. Bence bir sınıfın zengin bir arayüze sahip olması tercih edilir. SRP, bir nesnenin ortak sorumluluğuna girmeyen her şeyi ortak çalışanlara devretmesi gerektiği anlamına gelir. Bir kullanıcının yönetici rolü bir boolean alandan daha fazla ilgiliyse, kullanıcı nesnesinin PermissionsManager'a devredilmesi çok mantıklı olabilir. Ancak bu, bir kullanıcının isAdminyöntemini korumasının da iyi bir fikir olmadığı anlamına gelmez . Aslında, uygulamanız basitten karmaşığa geçiş yaptığında, Kullanıcı nesnesini kullanan kodu değiştirmek istediğiniz anlamına gelir. Şimdi, müşterinizin bir kullanıcının yönetici olup olmadığı sorusunu cevaplamanın gerçekte ne olduğunu bilmesine gerek yok.

... ama neden gerçekten bilmek istiyorsun?

Bununla birlikte, Bana bir kullanıcının belirli bir işlemi gerçekleştirmesine izin verilip verilmeyeceği gibi bir kullanıcının belirli bir işlemi gerçekleştirmesine izin verilip verilmeyeceği gibi bir kullanıcının bir yönetici olup olmadığını nadiren bilmeniz gerektiği gibi görünüyor. Bu durumda, Widget'taki gibi bir yöntem kullanmayı tercih ederim isUpdatableBy(User user):

boolean isUpdatableBy(User user) {
    return user.isAdmin();
}

Bu şekilde, bir Widget bir kullanıcı tarafından güncellenmesi için hangi kriterlerin yerine getirilmesi gerektiğini bilmekle sorumludur ve kullanıcı bir yönetici olup olmadığını bilmekle sorumludur. Bu tasarım niyetleri hakkında net bir şekilde iletişim kurar ve zamanı geldiğinde daha karmaşık iş mantığına geçmeyi kolaylaştırır.

[Düzenle]

Sorunu değil sahip olan User.isAdmin()ve kullanılmasıPermissionManager.userHasPermission(...)

Bir kullanıcının yönetici olup olmadığını (veya yönetici rolü olup olmadığını) bilmek istediğimde, neden Kullanıcı nesnesine bir yöntemi PermissionManager nesnesinde bir yöntemi çağırmaya tercih etmeyi tercih ettiğimi açıklamak için cevabımı ekleyeceğimi düşündüm.

Bence , bu kullanıcının bir yönetici olduğu sorusunu sormak istediğiniz yerde her zaman Kullanıcı sınıfına bağlı kalacağınızı varsaymak adil olur. Kurtulmayacağın bir bağımlılık. Ancak, kullanıcı hakkında soru sormak için kullanıcıyı farklı bir nesneye aktarmanız gerekirse, bu, Kullanıcıya zaten sahip olduğunuz nesnenin üstüne o nesneye yeni bir bağımlılık yaratır. Eğer soru çok sorulursa, fazladan bir bağımlılık yarattığınız çok fazla yer var, ve eğer bağımlılıktan herhangi biri talep ederse değiştirmek zorunda kalabileceğiniz çok fazla yer var.

Bunu bağımlılığı User sınıfına taşıyarak karşılaştırın. Şimdi, aniden, müşteri kodunun (soruyu sorması gereken kodun bu yönetici olduğu şeklinde ), bu sorunun nasıl yanıtlandığının uygulanmasına bağlanmadığı bir sisteme sahipsiniz . İzin sistemini tamamen değiştirmekte özgürsünüz ve bunu yapmak için yalnızca bir sınıftaki bir yöntemi güncellemeniz gerekiyor. Tüm müşteri kodu aynı kalır.

isAdminKullanıcı sınıfında izinler alt sistemine bağımlılık yaratma korkusuyla ilgili bir yönteme sahip olmamak konusunda ısrar etmek, bir kuruş kazanmak için bir dolar harcamak gibi bir şeydir. Elbette, Kullanıcı sınıfında bir bağımlılıktan kaçınırsınız, ancak soruyu sormanız gereken her yerde bir tane yaratmanın maliyeti vardır. İyi bir pazarlık değil.


2
"SRP, bir nesnenin ortak sorumluluğuna, kendi sorumluluğunda olmayan her şeyi devretmesi gerektiği anlamına gelir" - yanlış . SRP tanımı bir amacı, doğrudan yaptığı her şeyi kapsar ve delegeler her şeyi. Yalnızca bir görevi doğrudan yerine getiren, ancak delegasyon yoluyla dolaylı olarak diğer 50 görevi yerine getiren bir sınıf hala (muhtemelen) SRP'yi ihlal ediyor.
Aarona

KaptajnKold: Seninle aynı fikirdeyim çünkü sana katılıyorum ve bu konuda kendimi biraz suçlu hissediyorum. Gönderiniz tartışmaya eklenir, ancak soruyu yanıtlamaz.
GlenPeterson

@GlenPeterson Peki, "doğru" gibi göründüğü gibi olan bir soruyu ele almaya çalıştım. Ayrıca: Kesinlikle benimle aynı fikirde olduğun için kendini suçlu hissetmemelisin :)
KaptajnKold

1
@JamesSnell Belki. Eğer. Ancak bu, Kullanıcıdaki isAdmin yöntemi ile sınırlandırdığım bir uygulama detayı. Bu şekilde, müşteri kodunuzun bir Kullanıcının “yönetimi” bir boole alanı olmaktan daha gelişmiş bir şeye dönüşürken değişmesi gerekmez. Bir Kullanıcının yönetici olup olmadığını bilmeniz gereken birçok yer olabilir ve yetkilendirme sisteminiz her değiştiğinde bunları değiştirmek zorunda kalmazsınız.
KaptajnKold,

1
@JamesSnell Belki birbirimizi yanlış anlıyoruz? Sorusu bir kullanıcı bir yönetici olup olmadığı sorusuna aynı değildir bir kullanıcı, belirli bir eylemi gerçekleştirmek için izin olup olmadığını . İlk sorunun cevabı her zaman bağlamdan bağımsızdır. İkincisinin cevabı, içeriğe çok bağlıdır. Asıl cevabımın ikinci yarısında ele almaya çalıştım.
KaptajnKold,

1

Bu tartışma bana sınıf tasarımı, güvenlik ve doğruluk hakkında benzer bir tartışmada dikkatimi çeken Eric Lippert'in Blog Post'u hatırlattı.

Neden bu kadar çok Sınıf Sınıfı Mühürlendi?

Özellikle, Eric nokta yükseltir:

4) Güvenli. Polimorfizmin tüm amacı, hayvanlara benzeyen fakat aslında zürafalar olan nesnelerin etrafından geçebileceğinizdir. Burada potansiyel güvenlik sorunları var.

Mühürsüz bir türün örneğini alan bir yöntemi her uyguladığınızda, bu türün potansiyel olarak düşmanca örnekleri karşısında sağlam olacak şekilde yazmalısınız. SİZİN uygulamalarınız için doğru olduğunu bildiğiniz herhangi bir değişmeze güvenemezsiniz, çünkü bazı düşmanca web sayfaları uygulamanızı alt sınıflandırabilir, mantığınızı bozan şeyler yapmak için sanal yöntemleri geçersiz kılabilir ve onu geçebilir. , O sınıfı kullanan yöntemleri, o sınıfın ne yaptığını bildiğimden emin olarak yazabilirim.

Bu teğet gözükebilir, ancak sanırım diğer posterlerin SOLID tek sorumluluk ilkesiyle ilgili ortaya koydukları nokta ile uyuşmuyor . Bir User.IsAdminyöntemi veya özelliği uygulamamak için bir neden olarak aşağıdaki kötü amaçlı sınıfı düşünün .

public class MyUser : User
{

    public new boolean IsAdmin()
    {
        // You know it!
        return true;
    }

    // Anything else we can break today?

}

Tabii ki, bu bir gerginlik: kimse henüz IsAmdminsanal bir yöntem yapmayı önermedi, ancak kullanıcı / rol mimariniz tuhaf bir şekilde karmaşık hale geldiyse olabilir. (Ya da belki değil. Bir düşünün public class Admin : User { ... }: böyle bir sınıfın içinde yukarıdaki kodun aynısı olabilir.) Kötü niyetli bir ikili kodun yerine yerleştirilmesi yaygın bir saldırı vektörü değildir ve kaos için belirsiz bir kullanıcı kitaplığından çok daha fazla potansiyele sahiptir - o zaman yine, Gerçek shenaniganların kapısını açan Ayrıcalıklı Eskalasyon hatası olabilir . Son olarak, kötü niyetli bir ikili dosya veya nesne örneği çalışma zamanına girdiğinde, "Ayrıcalık veya Güvenlik Sisteminin" benzer bir şekilde değiştirildiğini hayal etmek çok da zor olmaz.

Fakat Eric'in dikkatini çektiğinizde, kullanıcı kodunuzu belirli bir hassas sisteme koyarsanız, belki de oyunu kaybedersiniz.

Oh, ve sadece kayıt için kesin olmak gerekirse, şu soruyu kabul ediyorum: “Bir kullanıcı bir Yönetici olup olmadığına karar vermemelidir. Ayrıcalıklar veya Güvenlik sistemi gerekir. ” User.IsAdminKod, sistem üzerinde çalışıyorsa, kodun% 100 kontrolünde kalmamanız için bir yöntem kötü bir fikirdir - bunun yerine yapmalısınız Permissions.IsAdmin(User user).


Ha? Bu nasıl alakalı? Güvenlik politikasını nereye koyduğunuz önemli değil, her zaman potansiyel olarak güvensiz bir şeyle alt sınıfa yerleştirebilirsiniz. Bir şeyin Kullanıcı, Müdür, Politika, İzinSet veya herhangi bir şekilde olması farketmez. Bu tamamen ilgisiz bir konu.
Aaron

Tek Sorumluluk lehine bir güvenlik argümanıdır. Amacınızı doğrudan cevaplamak için, güvenlik ve izinler modülü mühürlü bir sınıf olacak, ancak bazı tasarımlar Kullanıcı sınıfının sanal ve kalıtsal olmasını isteyebilir. Blog yazısındaki son nokta (belki de en az muhtemel / önemli olanıdır) ve belki de sorunları biraz sınırlandırıyorum, ama kaynak göz önüne alındığında, polimorfizmin güvenlik tehdidi olabileceği ve dolayısıyla sorumluluklarının sınırlandırılabileceği açıktır. verilen bir sınıf / nesne daha güvenlidir.
Patrick M,

Neden böyle söylediğinden emin değilim. Pek çok güvenlik ve izin çerçevesi oldukça genişletilebilir. Güvenliği (vb IPrincipal, IIdentity, ClaimSet,) ile ilgili çekirdek NET türlerinin çoğu yalnızca edilmez değil mühürlü ama ne istersen takmak böylece aslında arayüzleri veya soyut sınıflar bulunmaktadır. Eric'in bahsettiği şey, System.Stringkalıtsal olmak istemeyeceğiniz bir şeydir ; çünkü her türlü kritik çerçeve kodu, işleyişi hakkında çok özel varsayımlar yapar. Uygulamada, çoğu "kullanıcı kodu" (güvenlik dahil) test çiftlerine izin vermek için devralınabilir.
Aaron,

1
Her neyse, polimorfizm soruda hiçbir yerde belirtilmez ve yazarın orijinal yorumun yapıldığı yere olan bağlantısını takip ederseniz, genel olarak kalıtımdan ve hatta sınıfsal değişmezlerden bahsetmediği açıktır - sorumluluklarla ilgiliydi.
Aaron

Bahsetmediğini biliyorum, ancak sınıf sorumluluk ilkesi polimorfizmle doğrudan ilişkili. IsAdminGeçersiz kılmanın / birlikte seçmenin bir yöntemi yoksa, potansiyel bir güvenlik açığı yoktur. Bu sistem arayüzlerinin yöntemlerini gözden, IPrincipalve IIdentitybu tasarımcıların benimle hemfikir ve bu yüzden Kabul ediyoruz olduğu açıktır.
Patrick M,

0

Buradaki sorun şudur:

if (user.isAdmin()) {
   doPriviledgedOp();
} else {
   // maybe we can anyway!
   doPriviledgedOp();
}

Güvenliğinizi uygun şekilde kurdunuz ya da bu durumda ayrıcalıklı yöntem arayan kişinin yetkisine sahip olup olmadığını kontrol etmelidir - bu durumda kontrol gereksizdir. Ya da güvenlik konusunda kendi kararlarını vermesi için ayrıcalıklı olmayan bir sınıfa güvenmiyorsunuz ve güveniyorsunuz.


4
Ayrıcalıklı olmayan bir sınıf nedir?
Brandon

1
Bu tür kodların yazılmasını önlemek için yapabileceğiniz hiçbir şey yok. Uygulamanızı nasıl tasarladığınızdan bağımsız olarak, bir yerde bunun gibi açık bir çek olacak ve birisi güvensiz olmak için yazabilir. İzin sisteminiz bir rol veya izin özniteliğine sahip bir yöntemi dekore etmek kadar düzenli olsa bile - birileri yanlış bir "genel" veya "herkes" iznini yanlış atabilir. Bu yüzden, bunun User.IsAdmin özellikle bir problemi nasıl ortaya çıkardığını anlamıyorum .
Aarona
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.