Що означає "Користувач не повинен приймати рішення про те, чи це Адміністратор чи ні. Привілеї чи система безпеки повинні ".


55

Приклад, використаний у запитанні, передає мінімальну кількість даних щодо функції, що стосується найкращого способу визначення того, користувач є адміністратором чи ні. Одна поширена відповідь:

user.isAdmin()

Це підштовхнуло коментар, який повторювався кілька разів і багато разів голосувався:

Користувач не повинен вирішувати, чи це Адміністратор чи ні. Привілеї чи система безпеки повинні. Що те, що щільно поєднане з класом, не означає, що це гарна ідея зробити його частиною цього класу.

Я відповів,

Користувач нічого не вирішує. Об'єкт / таблиця користувача зберігає дані про кожного користувача. Фактичні користувачі не можуть змінити все про себе.

Але це не було результативним. Очевидно, що існує основна різниця в точці зору, яка ускладнює спілкування. Чи може хтось мені пояснити, чому user.isAdmin () поганий, і намалював короткий ескіз того, що це виглядає як зроблено "правильно"?

Дійсно, я не бачу переваги відокремлення безпеки від системи, яку вона захищає. Будь-який текст безпеки говорить про те, що безпеку потрібно розробляти в системі з самого початку і розглядати на кожному етапі розвитку, розгортання, обслуговування та навіть до кінця терміну експлуатації. Це не те, що можна затягнути на боці. Але 17 голосів на цей коментар поки що говорять про те, що я пропускаю щось важливе.


8
Я не вважаю його поганим, особливо якщо це лише прапор у таблиці db користувача. Якщо в майбутньому будуть зміни, змінити їх у майбутньому. Ви також можете викликати систему безпеки від об'єкта користувача. Я колись читав про модель дизайну, згідно з якою кожен доступ до db / ресурсів повинен проходити через об'єкт поточного користувача, щоб переконатися, що ви ніколи не намагаєтесь робити щось, що заборонено. Це може бути трохи більше, але користувач може надати об'єкт "привілеїв", який охоплює всі речі, пов'язані з безпекою.
Головопад

Я думаю, було непорозуміння. Для вас "Користувач" означає особу, яка використовує програмне забезпечення. Для них "Користувач" означає код, який використовує вашу функціональність.
Філіпп

2
Не те, що ви запитували, але я б найбільше насторожився в цьому випадку метод IsAdmin () взагалі, куди б ви його не ввели. Зазвичай я рекомендую, щоб "Адміністратор" не був жорстким кодом, а просто мати набір можливих дозволів, а "Адміністратор", як правило, мати весь набір. Таким чином, коли вам потрібно пізніше розділити роль адміністратора, все набагато простіше. І це скорочується за спеціальною логікою випадку.
psr

1
@Philipp Я не думаю, що так ... Обидва сайти прямо говорять про Userоб'єкт isAdmin()методом (що б там не було за кадром)
Izkata

Надто пізно для редагування; Я мав на увазі "Обидві сторони", а не "Обидва сайти" ... = P
Izkata

Відповіді:


12

Подумайте про Користувача як про ключ, а дозволи - як про значення.

Різні контексти можуть мати різні дозволи для кожного користувача. Оскільки дозволи мають відповідність контексту, вам доведеться змінити користувача для кожного контексту. Тож краще покласти цю логіку десь в іншому місці (наприклад, контекстний клас) і просто шукати дозволи, де це потрібно.

Побічним ефектом є те, що це може зробити вашу систему більш захищеною, оскільки ви не можете забути очистити прапор адміністратора на місцях, де це не актуально.


40

Цей коментар був погано сформульований.

Справа не в тому, чи "вирішує" об'єкт користувача, чи це адміністратор, пробний користувач, суперпользователь або щось інше або не в чомусь звичайному. Очевидно, вона не вирішує жодної з цих речей, це робить програмна система в цілому, як ви її впровадили. Справа в тому, чи " клас " користувача повинен представляти ролі головного, що представляють його об'єкти, чи це відповідальність іншого класу.


6
Ой (це був мій коментар), але ти все-таки висловив це набагато краще, ніж я.
Мар'ян Венема

Ви говорите про моделювання ролей в окремому класі ролей, а не в класі користувача? Ви не рекомендуєте окрему систему, як LDAP? Що робити, якщо кожне окреме рішення, яке приймає клас Ролі, вимагає запиту відповідної записи користувача та не потребує іншої інформації? Чи все-таки він повинен бути окремим від Користувача і якщо так, то чому?
GlenPeterson

2
@GlenPeterson: не обов'язково, просто класи <> таблиці, і зазвичай існує набагато більше класів, які слід розпізнати, ніж таблиці. Орієнтованість на дані призводить до визначення підлістів для класів, хоча, коли багато разів ці списки повинні бути визначені в іншій "великій книзі" (замовленнях) або якомусь такому класі, що передається користувачеві (або порядку), для отримання якого ... Це скоріше питання обов’язків, ніж дієслів та іменників.
Мар'ян Венема

9
Подумайте про Користувача як про ключ, а дозволи - як про значення. Різні контексти можуть мати різні дозволи для кожного користувача. Оскільки дозволи мають відповідність контексту, вам доведеться або змінити користувача для кожного контексту. Тож краще ви поставите цю логіку десь в іншому місці (наприклад, контекстний клас).
Вільберт

2
@Wilbert: Контекст! Це особливо просвічує! Так, якщо у вас є більше одного контексту для цих дозволів, то для мене все має сенс. Зазвичай я бачу ці дозволи в одному контексті, а наклеювання їх на користувача є найпростішим рішенням у цьому випадку. Очевидно, що вони там не належать, якщо застосовують по-різному у другому контексті. Якщо ви напишете це як відповідь, я, швидше за все, прийму це. Дякую.
GlenPeterson

30

Я б не вирішив сформулювати оригінальний коментар таким чином, як він був написаний, але він визначає потенційно законну проблему.

Зокрема, проблеми, які вимагають розділення ордера, - це автентифікація та авторизація .

Під автентифікацією розуміється процес входу та отримання ідентичності. Це те, як системи знають, хто ви є , і використовуєте їх для таких речей, як персоналізація, право власності на об'єкти тощо.

Авторизація стосується того, що вам дозволено робити , і це (як правило) не визначається тим, хто ви є . Натомість це визначається якоюсь політикою безпеки, наприклад ролями чи дозволами, які не цікавляться такими речами, як ваше ім’я чи електронна адреса.

Ці два можуть змінюватися ортогонально один до одного. Наприклад, ви можете змінити модель аутентифікації, додавши провайдерів OpenID / OpenAuth. І ви можете змінити політику безпеки, додавши нову роль або перейшовши з RBAC на ABAC.

Якщо все це відноситься до одного класу чи абстракції, то ваш код безпеки, який є одним з ваших найважливіших інструментів для зменшення ризику , стає, за іронією долі, високим ризиком.

Я працював із системами, де автентифікація та авторизація були занадто щільно поєднані. В одній системі було дві паралельні бази даних користувачів, кожна для одного типу "ролі". Людина або команда, яка розробила це, очевидно, ніколи не вважали, що один фізичний користувач може бути в обох ролях, або що можуть бути певні дії, спільні для декількох ролей, або що можуть виникнути проблеми зі зіткненнями User ID. Це, безумовно, екстремальний приклад, але працювати з цим було / неймовірно болісно.

Microsoft та Sun / Oracle (Java) називають сукупність відомостей про автентифікацію та авторизацію як про принципи безпеки . Це не ідеально, але працює досить добре. В .NET, наприклад, у вас є IPrincipal, що інкапсулюєIIdentity - колишнє бути політик (авторизація) об'єктом , а останній є ідентичністю (аутентифікація). Можна обґрунтовано ставити під сумнів рішення поставити один всередині іншого, але головне в тому , що велика частина коду ви пишете буде тільки для одного з абстракцій , що означає , що це легко перевірити і рефакторинг.

З User.IsAdminполем немає нічого поганого ... якщо тільки не існує User.Nameполя. Це вказувало б на те, що концепція "Користувач" неправильно визначена, і це, на жаль, дуже поширена помилка серед розробників, які трохи мокрі за вухами, коли мова йде про безпеку. Як правило, єдине, на що слід ділитися ідентичністю та політикою, - це ідентифікатор користувача, який, не випадково, саме те, як він реалізований і в моделях безпеки Windows, і * nix.

Цілком прийнятно створювати об’єкти обгортки, які інкапсулюють ідентичність, і політику. Наприклад, це полегшить створення екрана інформаційної панелі, де потрібно відобразити повідомлення "привіт" на додаток до різних віджетів або посилань, до яких поточний користувач може отримати доступ. Поки ця обгортка просто зафіксує інформацію про політику та інформацію про політику, і не вимагає її володіти. Іншими словами, доки він не представлений як сукупний корінь .

Спрощена модель безпеки завжди здається гарною ідеєю, коли ви вперше розробляєте нову програму через YAGNI і все це, але вона майже завжди повертається, щоб кусати вас пізніше, адже, несподіванка, сюрприз, нові функції додаються!

Отже, якщо ви знаєте, що для вас найкраще, ви зберігатимете інформацію про автентифікацію та авторизацію окремо. Навіть якщо прапор "авторизації" зараз такий же простий, як прапор "IsAdmin", вам все одно буде краще, якщо він не входить до того ж класу або таблиці, що і інформація про автентифікацію, так що якщо і коли ваша політика безпеки потребує Зміна, вам не потрібно робити реконструктивні операції на системах аутентифікації, що вже працює добре.


11
О, я б хотів, щоб у мене був час написати це. Знову ж таки, я, мабуть, не зміг би це зробити так добре, як ви це зробили без набагато більшої думки з мого боку. Багато разів мій інстинкт кишки підказує мені йти тим чи іншим шляхом, але пояснення, чому - собі чи комусь іншому - забирає набагато більше думок та зусиль. Дякую за цю публікацію Був би його багаторазовим, але SE не дозволить мені ...
Marjan Venema

Це звучить шокуюче схоже на проект, над яким я працюю, і ваша відповідь дала мені ще одну точку зору. Дуже дякую!
Брендон

"З полем User.IsAdmin немає нічого поганого ... якщо ви також не пишете поле User.Name". Але що isAdminтаке метод? Він може просто делегувати об'єкт, відповідальність за який слідкує за дозволами. Що є найкращою практикою в розробці реляційної моделі (якими полями володіє сутність), не обов'язково перекладається на те, що є найкращою практикою в моделі OO (на які повідомлення може відповісти об’єкт).
KaptajnKold

@KaptajnKold: Я продовжую читати це почуття в різних питаннях на цьому форумі. Якщо метод існує, не має значення, чи він безпосередньо виконує операції, або делегує їх, він все ще вважається відповідальністю, оскільки інші класи можуть на нього покладатися . Можливо, User.IsAdminви будете однолінійним, що не робить нічого, окрім дзвінків PermissionManager.IsAdmin(this.Id), але якщо на нього посилаються 30 інших класів, ви не можете його змінити або видалити. Ось чому СРП існує.
Aaronaught

І, @KaptajnKold, що стосується реляційних моделей, я не впевнений, до чого ти звертаєшся; ще гірше мати IsAdmin поле . Це поле, яке, як правило, затримується довгий час після того, як система перетворилася в RBAC або щось складніше, і поступово виходить із синхронізації з "реальними" дозволами, і люди забувають, що вони більше не повинні її використовувати, і ... ну просто скажіть ні. Навіть якщо ви думаєте, що у вас є лише дві ролі, "admin" і "non-admin", не зберігайте їх як булеве / бітове поле, нормалізуйтесь належним чином і не майте таблиці дозволів.
Aaronaught

28

Ну, принаймні, це порушує принцип єдиної відповідальності . Якщо мають бути зміни (декілька рівнів адміністрування, ще більше різних ролей?) Такий дизайн буде досить брудним і жахливим у підтримці.

Розглянемо перевагу відокремлення системи безпеки від класу користувача, щоб обидва могли мати одну, чітко визначену роль. Ви закінчите з чистішим, простішим у підтримці дизайну в цілому.


2
@GlenPeterson: Ні, користувачі можуть дуже добре існувати без безпеки. Як щодо мого імені, мого відображуваного імені, моєї адреси, моєї адреси електронної пошти тощо. Де ви б їх помістили? Ідентифікація (автентифікація) та авторизація - це взагалі різні звірі.
Мар'ян Венема

2
class Userє основним компонентом триплету безпеки Ідентифікації, Автентифікації, Авторизації . Вони тісно пов'язані на рівні дизайну, тому нерозумно код відображати цю взаємозалежність.
MSalters

1
Питання полягає в тому, чи повинен клас User містити всю логіку поводження з триплетом!
Захід

3
@MSalters: якщо це API такої логіки, він знає про це, навіть якщо делегує в іншому місці точну логіку. Слід зазначити, що Авторизація (дозволи) - це поєднання користувача з чимось іншим, і тому воно не повинно бути частиною ні користувача, ні чогось іншого, а частиною систем дозволів, які правильно знають і користувачі, і будь-що інше отримують дозвіл на.
Мар'ян Венема

2
Should there be changes- ну, ми не знаємо, що відбудуться зміни. ЯГНИ і все. Чи змінюються ті зміни, коли це стає необхідним, правда?
Ізката

10

Я думаю, що питання, можливо, висловлене неякісно, ​​полягає в тому, що воно надає занадто велику вагу Userкласу. Ні, немає проблем із безпекою щодо використання методу User.isAdmin(), як було запропоновано в цих коментарях. Зрештою, якщо хтось є достатньо глибоким у вашому коді, щоб ввести зловмисний код для одного з ваших основних класів, у вас є серйозні проблеми. З іншого боку, не має сенсу Userвирішувати, чи є він адміністратором для кожного контексту. Уявіть, що ви додаєте різні ролі: модератори для свого форуму, редактори, які можуть публікувати публікації на головній сторінці, письменники, які можуть писати вміст для редакторів, щоб публікувати тощо. З наближенням розміщення його на Userоб'єкті ви виявите занадто багато методів і занадто багато логіки, що живуть на тому, Userщо безпосередньо не пов'язане з класом.

Натомість ви можете використовувати перелік різних типів дозволів та PermissionsManagerклас. Можливо, у вас може бути такий метод, як PermissionsManager.userHasPermission(User, Permission)повернення булевого значення. На практиці це може виглядати так (у java):

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

Він по суті еквівалентний методу Rails before_filter, який надає приклад перевірки цього типу дозволів у реальному світі.


6
Чим це відрізняється user.hasPermission(Permissions.EDITOR), за винятком того, що він є більш детальним та процедурним замість ОО?
Головопад

9
@Arian: тому що user.hasPermissionsвкладає занадто багато обов'язків у клас користувача. Це потребує того, щоб користувач знав про дозволи, тоді як користувач (клас) повинен мати можливість існувати без таких знань. Ви також не хочете, щоб клас користувача знав про те, як друкувати або відображати (створювати форму) сам, ви залишаєте це рендерером / конструктором принтера або класу рендерера / конструктора дисплея.
Мар'ян Венема

4
user.hasPermission(P)є правильним API, але це лише інтерфейс. Справжнє рішення, безумовно, повинно прийматися в підсистемі безпеки. Щоб вирішити коментар Syrion щодо тестування, під час тестування ви можете змінити цю підсистему. Однак вигода відвертої user.hasPermission(P)полягає в тому, що ви не забруднюєте весь код лише для того, щоб можете протестувати class User.
MSalters

3
@MarjanVenema За такою логікою Користувач залишатиметься об'єктом даних, не маючи жодних обов'язків. Я не кажу, що все управління дозволами має бути реалізовано в класі користувача, якщо є необхідність у такій складній системі. Ви кажете, що користувачеві не потрібно знати про дозволи, але я не бачу, чому кожен інший клас повинен знати, як вирішити дозволи для користувача. Це набагато важче робить глузування та тестування.
Головопад

6
@Arian: хоча анемічні класи - це запах, деякі класи просто не мають жодної поведінки ... Користувач, imho, це один такий клас, який краще використовувати як параметр для цілої кількості інших речей (які хочуть робити речі / приймати рішення, виходячи з того, хто їх просить), ніж використовувати його як контейнер для будь-якої поведінки лише тому, що зручно це кодувати.
Мар'ян Венема

9

Object.isX () використовується для представлення чіткого зв’язку між об'єктом і X, який може бути виражений булевим результатом, наприклад:

Water.isBoiling ()

Circuit.isOpen ()

User.isAdmin () передбачає єдине значення "Адміністратор" у кожному контексті системи , що користувач у кожній частині системи є адміністратором.

Хоча це звучить досить просто, програма справжнього світу майже ніколи не підходить до цієї моделі, але, звичайно, буде потреба в користуванні адміністратором [X] ресурсу, але не [Y], або для більш чітких типів адміністраторів ([проект ] адміністратор проти [системного] адміністратора).

Зазвичай ця ситуація вимагає дослідження вимог. Рідко, якщо коли-небудь, клієнт захоче відносини, які насправді представляє User.isAdmin (), тож я б вагався реалізувати будь-яке таке рішення без уточнення.


6

Плакат просто мав на увазі інший і складніший дизайн. На мою думку, User.isAdmin()це добре . Навіть якщо ви пізніше представите якусь складну модель дозволів, я бачу мало підстав рухатися User.isAdmin(). Пізніше, можливо, ви захочете розділити клас користувача на об’єкт, що представляє зареєстрованого користувача, і статичний об'єкт, що представляє дані про користувача. А може і не. Збережіть завтра на завтра.


4
Save tomorrow for tomorrow. Так. Коли ви зрозумієте, що щільно зв'язаний код, який ви тільки що написали, придурив вас, і вам доведеться його переписати, тому що не потрібно було зайвих 20 хвилин, щоб його роз'єднати ...
MirroredFate

5
@MirroredFate: Цілком можливо, щоб User.isAdminметод був підключений до довільного механізму дозволів, не зв'язуючи клас користувача з цим конкретним механізмом.
кевін клайн

3
Для того, щоб User.isAdmin поєднувався з довільним механізмом дозволів, навіть без з'єднання з певним механізмом, потрібно ... добре ... з'єднання. Мінімальний показник - інтерфейс. І цього інтерфейсу просто не повинно бути там. Це надання класу користувача (та інтерфейсу) те, за що він просто не повинен нести відповідальність. Aaronaught пояснює це набагато краще, ніж я можу (як і поєднання всіх інших відповідей на це питання).
Мар'ян Венема

8
@MirroredFate: Мені все одно, чи доведеться переписати просту реалізацію, щоб відповідати більш складним вимогам. Але я мав по-справжньому поганий досвід із надмірно складними API, розробленими для задоволення уявлених майбутніх вимог, які ніколи не виникали.
кевін клайн

3
@kevincline Хоча надмірно складні API - це (за визначенням) погана річ, я не пропонував писати надмірно складні API. Я констатував, що Save tomorrow for tomorrowце жахливий проектний підхід, оскільки він створює проблеми, які були б тривіальними, якби система була реалізована правильно набагато гірше. Насправді цей менталітет - це те, що призводить до надмірно складних API, оскільки додається шар за шаром, щоб виправити початковий поганий дизайн. Я думаю, моя позиція така measure twice, cut once.
MirroredFate

5

Насправді не проблема ...

Я не бачу проблем з User.isAdmin(). Я, звичайно, вважаю за краще щось подібне PermissionManager.userHasPermission(user, permissions.ADMIN), що в святому імені SRP робить код менш зрозумілим і не додає нічого цінного.

Я думаю, що SRP дещо тлумачиться буквально. Я думаю, що це добре, навіть класу краще мати багатий інтерфейс. SRP просто означає, що об'єкт повинен делегувати колабораторам все, що не підпадає під його єдину відповідальність. Якщо в ролі адміністратора користувача є щось більше, ніж булеве поле, це може дуже добре мати об’єкт користувача делегувати PermissionsManager. Але це не означає, що користувачеві це також не дуже зручна ідея зберегти його isAdminметод. Насправді це означає, що коли ваша програма закінчується з простого на складний, вам потрібно буде змінити код, який використовує об’єкт User. IOW, ваш клієнт не повинен знати, що потрібно насправді, щоб відповісти на запитання, чи є користувач адміністратором чи ні.

... але чому ти насправді хочеш знати?

При цьому мені здається, що вам рідко потрібно знати, чи користувач є адміністратором, крім того, щоб мати можливість відповісти на якесь інше питання, наприклад, чи дозволено користувачеві виконувати якусь конкретну дію, скажімо, як оновлення віджета. Якщо це так, я вважаю за краще використовувати такий метод на Віджеті, як isUpdatableBy(User user):

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

Таким чином віджет несе відповідальність за те, щоб знати, яким критеріям повинно бути задоволено оновлення користувачем, а користувач несе відповідальність за те, чи він адміністратор. Цей дизайн чітко повідомляє про свої наміри та полегшує перехід до більш складної бізнес-логіки, якщо і коли настане цей час.

[Редагувати]

Проблема НЕ маючи User.isAdmin()і використовуючиPermissionManager.userHasPermission(...)

Я думав, що додам свою відповідь, щоб пояснити, чому я віддаю перевагу виклику методу на об’єкті користувача, аніж виклику методу на об'єкт PermissionManager, якщо я хочу знати, чи користувач є адміністратором (чи має роль адміністратора).

Я думаю, що справедливо припустити, що ви завжди будете залежати від класу користувача, де б вам не було задавати питання , це адміністратор цього користувача? Це залежність, якої ви не можете позбутися. Але якщо вам потрібно передати користувачеві інший об'єкт, щоб задати питання про нього, це створює нову залежність від цього об’єкта поверх тієї, яку ви вже маєте у Користувача. Якщо запитання задають багато, це багато місць, де ви створюєте додаткову залежність, і це може бути багато місць, які вам доведеться змінити, якщо цього вимагатиме будь-яка із залежностей.

Порівняйте це з переміщенням залежності в клас користувача. Тепер раптом у вас є система, де клієнтський код (код, на який потрібно задати питання , це користувач-адміністратор ) не поєднаний з реалізацією того, як на це питання отримують відповідь. Ви можете повністю змінити систему дозволів, і вам потрібно лише оновити один метод в одному класі, щоб це зробити. Весь код клієнта залишається однаковим.

Наполягаючи на відсутності у користувача isAdminметоду, побоюючись створити залежність класу користувача від підсистеми дозволів, на мій погляд, схоже на те, щоб витратити долар, щоб заробити ні копійки. Звичайно, ви уникаєте однієї залежності в класі користувача, але ціною створення її в кожному місці, де вам потрібно задати питання. Не хороший торг.


2
"SRP просто означає, що об'єкт повинен делегувати колабораторам все, що не належить до його єдиної відповідальності" - false . Визначення SRP охоплює все, що об'єкт робить безпосередньо, і все, що він делегує. Клас, який виконує лише одне завдання безпосередньо, але виконує 50 інших завдань опосередковано через делегування, все ще (ймовірно) порушує СРП.
Aaronaught

KaptajnKold: Я поставив +1, тому що я згоден з вами, і я відчуваю трохи провини в цьому. Ваше повідомлення додає до обговорення, але воно не відповідає на питання.
GlenPeterson

@GlenPeterson Ну, я спробував вирішити частину питання, яке було "як це виглядає зроблено" правильно "" Також: Ви абсолютно не повинні почувати себе винними, погодившись зі мною :)
KaptajnKold

1
@JamesSnell Можливо. Якщо. Але це детальна інформація про реалізацію, яку я все-таки обмежуватимуться в методі isAdmin для користувача. Таким чином, ваш код клієнта не повинен змінюватися, коли «адмін-ність» користувача змінюється від булевого поля до чогось більш досконалого. У вас може бути багато місць, де вам потрібно знати, чи є Користувач адміністратором, і вам не потрібно змінювати їх щоразу, коли ваша система авторизації змінюється.
KaptajnKold

1
@JamesSnell Може, ми неправильно розуміємо один одного? Питання про те, чи є користувач адміністратором, чи не відповідає питання про те, дозволено чи ні користувачеві виконувати певну дію . Відповідь на перше питання завжди не залежить від контексту. Відповідь на друге дуже сильно залежить від контексту. Це те, що я намагався звернутись у другій половині своєї первинної відповіді.
KaptajnKold

1

Ця дискусія нагадала мені допис у блозі Еріка Ліпперта, який був мені відомий у подібній дискусії про дизайн класу, безпеку та правильність.

Чому стільки рамкових класів запечатано?

Зокрема, Ерік піднімає питання:

4) Захищений. вся суть поліморфізму полягає в тому, що ви можете обходити об'єкти, схожі на тварин, але насправді є жирафами. Тут є потенційні проблеми безпеки.

Кожен раз, коли ви реалізуєте метод, який приймає екземпляр незапечатаного типу, ви ОБОВ'ЯЗКОВО пишете цей метод, щоб бути надійним перед обличчям потенційно ворожих випадків цього типу. Ви не можете покладатися на будь-які інваріанти, які, на вашу думку, відповідають вашим реалізаціям, тому що якась ворожа веб-сторінка може підкласифікувати вашу реалізацію, замінити віртуальні методи, щоб робити речі, які заплутують вашу логіку, і передає їх. Кожен раз, коли я запечатую клас , Я можу написати методи, які використовують цей клас із впевненістю, що я знаю, що робить цей клас.

Це може здатися дотичною, але я думаю, що це узгоджується з тією точкою, яку інші плакати підняли щодо принципу SOLID єдиної відповідальності . Розглянемо наступний шкідливий клас як причину не застосовувати User.IsAdminметод чи властивість.

public class MyUser : User
{

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

    // Anything else we can break today?

}

Зрозуміло, це трохи розтягнутий: ще ніхто не пропонував зробити IsAmdminвіртуальний метод, але це може статися, якщо ваша архітектура користувача / ролі виявиться надзвичайно складною. (А може і ні. Поміркуйте public class Admin : User { ... }: такий клас може мати саме той код вище в ньому.) Введення шкідливого бінарного файлу на місце не є звичайним вектором атаки і має набагато більше потенціалу для хаосу, ніж малозрозуміла бібліотека користувачів - тоді знову ж таки, це могла бути помилка Privilege Escalation, яка відкриває двері справжнім шенанігам. Нарешті, якщо зловмисний бінарний або об’єктний об'єкт знайшов свій час у процесі виконання, уявити "Привілей або систему безпеки" замінено аналогічно.

Але враховуючи серце Еріка, якщо ви ввели код користувача у певний тип вразливої ​​системи, можливо, ви просто програли гру.

О, і щоб бути точним для запису, я погоджуюся з постулатом у запитанні: «Користувач не повинен вирішувати, Адмініструє він чи ні. Привілеї або система безпеки повинні. " User.IsAdminМетод - погана ідея, якщо ви запускаєте код у системі, ви не залишаєтесь під 100% контролем коду - замість цього слід робити Permissions.IsAdmin(User user).


Так? Як це доречно? Незалежно від того, де ви розмістили політику безпеки, ви завжди могли б підкласифікувати її в щось небезпечне. Не має значення, чи є щось користувачем, принципалом, політикою, PermissionSet чи іншим. Це абсолютно не пов'язане питання.
Aaronaught

Це аргумент безпеки на користь єдиної відповідальності. Щоб безпосередньо відповісти на вашу думку, модуль безпеки та дозволів буде проклятим класом, але певні конструкції можуть бажати, щоб клас користувача був віртуальним та успадкованим. Це останній пункт у статті блогу (таким чином, мабуть, найменш вірогідний / важливий?), І, можливо, я трохи плутаю питання, але враховуючи джерело, зрозуміло, що поліморфізм може бути загрозою безпеці, а отже, обмежуючи обов'язки даний клас / об'єкт є більш захищеним.
Патрік М

Я не впевнений, чому ви це говорите. Багато рамок безпеки та дозволів є дуже розширюваними. Більшість основних типів .NET, пов’язаних із захистом (IPrincipal, IIdentity, ClaimSet тощо), не тільки не запечатані, але насправді є інтерфейсами або абстрактними класами, щоб ви могли підключати все, що завгодно. Що Ерік говорить, це те, що ви не хочете, щоб щось схоже System.Stringбуло у спадщину, оскільки всі види критичного рамкового коду роблять дуже конкретні припущення щодо того, як це працює. На практиці більшість «кодів користувача» (включаючи безпеку) успадковується, щоб мати можливість тестувати парні.
Aaronaught

1
У всякому разі, про поліморфізм ніде не йдеться у питанні, і якщо ви переходите за посиланням автора на те, де був зроблений оригінальний коментар, зрозуміло, що мова не йшла про спадщину або навіть про інваріанти класу - це стосувалася обов'язків.
Aaronaught

Я знаю, що про це не згадувалося, але принцип класової відповідальності безпосередньо пов'язаний з поліморфізмом. Якщо не існує IsAdminспособу переохочення / співпраці, потенційний отвір у захисті не існує. Переглядаючи методи цих системних інтерфейсів, IPrincipalі IIdentityзрозуміло, що дизайнери не згодні зі мною, і тому я визнаю суть.
Патрік М

0

Тут проблема:

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

Або ви належним чином налаштували свою безпеку, і в цьому випадку привілейований метод повинен перевірити, чи має абонент достатньо повноважень - у такому випадку перевірка є зайвою. Або ви не маєте і довіряєте непривілейованому класу, щоб приймати власні рішення щодо безпеки.


4
Що таке непривілейований клас?
Брендон

1
Ви насправді нічого не можете зробити, щоб запобігти написанню такого коду. Незалежно від того , як ви розробляєте додаток, де - то там не буде явною перевірки , як це, і хто - то міг би написати , що це небезпечно. Навіть якщо ваша система дозволів настільки обтічна, як декорування методу з атрибутом ролі чи дозволу - хтось може неправильно кинути на нього дозвіл "public" або "all". Тому я не бачу, як це робить такий метод, як User.IsAdmin конкретно проблема.
Aaronaught
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.