ACL та контролери
Перш за все: це найчастіше різні речі / шари. Коли ви критикуєте зразковий код контролера, він поєднує і те, і інше - очевидно, занадто жорстке.
tereško вже окреслив спосіб, як можна більше роз’єднати це за допомогою декоративного малюнка.
Я б спершу сходив крок назад, щоб шукати оригінальну проблему, з якою ви стикаєтесь, і трохи потім обговорити її.
З одного боку, ви хочете мати контролери, які просто виконують роботу, яку їм наказано (команда або дія, назвемо це командою).
З іншого боку, ви хочете мати змогу помістити ACL у вашу заявку. Сфера роботи цих ACL повинна бути - якщо я правильно зрозумів ваше запитання - керувати доступом до певних команд ваших програм.
Отже, такий вид контролю доступу потребує чогось іншого, що об’єднує цих двох. Виходячи з контексту, в якому виконується команда, починається ACL і потрібно робити рішення про те, чи може конкретна команда виконуватись конкретним суб'єктом (наприклад, користувачем).
Давайте підсумуємо до цього моменту те, що ми маємо:
- Командування
- ACL
- Користувач
Компонент ACL тут є центральним: він повинен знати хоча б щось про команду (щоб ідентифікація команди була точною), і вона повинна мати можливість ідентифікувати користувача. Користувачів, як правило, легко ідентифікувати за допомогою унікального ідентифікатора. Але часто у веб-додатках є користувачі, які взагалі не ідентифікуються, їх часто називають гостем, анонімними, усіма тощо. Для цього прикладу ми припускаємо, що ACL може споживати об’єкт користувача та інкапсулювати ці дані. Об'єкт користувача прив'язаний до об'єкта запиту програми, і ACL може споживати його.
Що з ідентифікацією команди? Ваша інтерпретація шаблону MVC передбачає, що команда складається з імені класу та імені методу. Якщо ми уважніше розглянемо, для команди існують навіть аргументи (параметри). Тож правильно запитати, що саме ідентифікує команду? Ім'я класу, ім'я методу, кількість або імена аргументів, навіть дані всередині будь-якого з аргументів або суміш всього цього?
Залежно від того, на якому рівні деталізації вам потрібно визначити команду у вашому ACL'ing, це може сильно відрізнятися. Для прикладу давайте збережемо його просто і вкажемо, що команда ідентифікується за назвою класу та методом.
Тож контекст того, як ці три частини (ACL, Command та User) належать одна одній, тепер є більш зрозумілим.
Можна сказати, що з уявним вмістом ACL ми вже можемо зробити наступне:
$acl->commandAllowedForUser($command, $user);
Просто подивіться, що тут відбувається: зробивши і команду, і користувача ідентифікованими, ACL може зробити це ефективно. Робота ACL не пов'язана з роботою як об'єкта користувача, так і конкретної команди.
Не вистачає лише однієї частини, вона не може жити в повітрі. І це не так. Отже, вам потрібно знайти місце, де повинен входити контроль доступу. Давайте подивимося, що відбувається у стандартному веб-застосуванні:
User -> Browser -> Request (HTTP)
-> Request (Command) -> Action (Command) -> Response (Command)
-> Response(HTTP) -> Browser -> User
Щоб знайти це місце, ми знаємо, що це повинно бути перед виконанням конкретної команди, тому ми можемо зменшити цей список і потрібно лише розглянути наступні (потенційні) місця:
User -> Browser -> Request (HTTP)
-> Request (Command)
У певний момент у вашій програмі ви знаєте, що конкретний користувач просив виконати конкретну команду. Ви вже робите тут певний ACL: якщо користувач запитує команду, яка не існує, ви не дозволяєте виконувати цю команду. Тож де б це не траплялося у вашій програмі, можливо, було б гарним місцем для додавання "справжніх" перевірок ACL:
Команда була знайдена, і ми можемо створити її ідентифікацію, щоб ACL впорався з нею. Якщо команда не дозволена користувачеві, команда не буде виконана (дія). Можливо, CommandNotAllowedResponse
замість CommandNotFoundResponse
запиту для справи запит не може бути вирішений за допомогою конкретної команди.
Місце, де відображення конкретного HTTPRequest відображається на команді, часто називається маршрутизацією . Оскільки маршрутизація вже має завдання знайти команду, чому б не розширити її, щоб перевірити, чи дійсно команда дозволена для ACL? Наприклад , за допомогою розширення Router
до маршрутизатора обізнані ACL: RouterACL
. Якщо ваш маршрутизатор ще не знає User
, то Router
це не те місце, тому що для роботи ACL'а повинна бути визначена не тільки команда, але й користувач. Отже, це місце може відрізнятися, але я впевнений, що ви можете легко знайти місце, яке вам потрібно розширити, оскільки саме це місце заповнює вимоги користувача та команди:
User -> Browser -> Request (HTTP)
-> Request (Command)
Користувач доступний з самого початку, Команда спочатку з Request(Command)
.
Отже, замість того, щоб поміщати перевірки ACL у конкретну реалізацію кожної команди, ви розміщуєте її перед нею. Вам не потрібні важкі шаблони, магія чи що завгодно, ACL робить свою роботу, користувач виконує свою роботу, і особливо команда робить свою роботу: Просто команда, нічого іншого. Команда не зацікавлена знати, застосовуються до неї ролі чи ні, якщо вона десь охороняється чи ні.
Тож просто тримайте речі, які не належать одне одному. Використовуйте трохи переформулювання принципу єдиної відповідальності (SRP) : Для зміни команди має бути лише одна причина - тому що команда змінилася. Не тому, що тепер ви вводите ACL'ing у свою заявку. Не тому, що ви перемикаєте об'єкт User. Не тому, що ви переходите з інтерфейсу HTTP / HTML на SOAP або інтерфейс командного рядка.
ACL у вашому випадку контролює доступ до команди, а не саму команду.
if($user->hasFriend($other_user) || $other_user->profileIsPublic()) $other_user->renderProfile()
(інакше відобразити "Ви не маєте доступу до профілю цього користувача" чи щось подібне? Я цього не