Створення списку контролю доступу (ACL), входу на сайт

14

Від автора: Більшість підручників мають справу тільки з встановленням особи користувача, що дозволяє враховувати два рівня безпеки: увійшли в систему і не увійшли. Для багатьох сайтів необхідний більш досконалий рівень контролю для керівництва тим, куди користувачам можна зайти і що можна робити. Створення списку системи контролю доступу (ACL) дасть вам можливість застосувати гнучкість в управлінні правами доступу.

Перед тим, як ми приступимо до створення списку контролю доступу на сайт, я Вам рекомендую завантажити вихідні коди.

Вступ

Створення списку контролю доступу (ACL), входу на сайт

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

Ваша проблема:

Ви хочете обмежити відвідування користувачів тільки тими сторінками, до яких є доступ відповідно до їх запису.

Рішення:

Виконання списку контролю доступу дозволить здійснювати управління можливістю користувачів отримати доступ до чого-небудь на вашому сайті або не отримати.

При перегляді демоверсії, доступною з скачуваним вихідним кодом, вас зустріне класифікаційна сторінка (index page), яка визначає список контролю доступу (ACL) для кожного користувача. Можна вибрати різні посилання внизу сторінки, щоб переглянути ACL для різних користувачів. Якщо клацнути на посилання ‘Admin Screen’ вгорі сторінки, можна подивитися приклад інтерфейсу адміністратора, що дозволяє управляти користувачами, рольовими іменами і правами доступу. ПРИМІТКА: система адміністратора буде виконувати оновлення бази даних кожні 30 хвилин, щоб переконатися, що все йде відмінно.

Ця система дасть можливість створювати різні групи користувачів (тобто осіб, елітних користувачів, співробітників і адмінів). Ми зможемо встановити унікальні права доступу для кожної групи, так само як і для окремих користувачів. Давайте почнемо з установки бази даних MySQL.

Крок 1: створюємо базу даних

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

Створення списку контролю доступу (ACL), входу на сайт

Код для створення бази даних доступний у вихідних файлах (install.sql), а ще там є інший файл (sampleData.sql), який створить 4 пробних користувача поряд з кількома рольовими іменами і правами доступу, яких можна буде протестувати. Просто відкрийте файли за допомогою свого улюбленого текстового редактора і скопіювати/вставити код в панель SQL в phpMyAdmin.

Крок 2: приєднуємо базу даних

Нам потрібно створити включається файл для того, щоб можна було з’єднуватися зі своєю базою даних. Створіть файл з назвою assets/php/database.php і додайте в нього наступний код (замініть змінні величини на інформацію, відповідну до свого хостинговому положення):

У першій рядку коду ми викликаємо session_start(); насправді ми не будемо використовувати змінні сеансу, але вони знадобляться нам як частина системи користувальницьких логінів. Потім ми викликаємо ob_start() для створення вихідного буфера. Зазвичай коли PHP створює сторінку, вона надсилається на браузер в процесі освіти. При використанні ob_start() сторінка і заголовки не надсилаються на браузер, поки вони не повністю завантажені, або поки ми не викличемо ob_end_flush(). Буферизуя сторінку, ми можемо переспрямувати використання PHP в будь-яку точку на сторінці, а не тільки її початок. Після висилки заголовків ми може перенаправляти тільки за допомогою JavaScript. Ініціативний хакер міг би з легкістю виключити JavaScript і подивитися нашу незахищену сторінку в усьому її пишноті. Ця єдина рядок дозволяє перешкоджати доступу користувача в будь-якій своїй частині, якщо потрібно.

Рядки 4-8 встановлюють змінні. $hasDB — це логічне вираз, що використовується для визначення наявності з’єднання. $server,$user, $pass, і $db – докази з’єднання для сервера. Рядок 9 з’єднує з сервером в той час, як рядок 10 визначає, чи з’єднання успішним. Якщо було, ми вибираємо базу даних для використання; якщо ні, відображаємо повідомлення про помилку за допомогою die().

Крок 3: створюємо клас ACL

Цей крок досить тривалий, так як ми створюємо клас ACL, який сформує основу нашої системи. Заздалегідь вибачаюся за його протяжність.

Створення списку контролю доступу (ACL), входу на сайт

Наша система ACL буде об’єктно-орієнтованої, так що давайте стартуємо з створення класифікаційного файлу. Починаємо з додавання визначення класу, визначення змінної і проектувальника файлу /assets/php/class.acl.php:

userID = floatval($userID);
} else {
$this->userID = floatval($_SESSION[‘userID’]);
}
$this->userRoles = $this->getUserRoles(‘ids’);
$this->buildACL();
}
function ACL($userID=»)
{
$this->__constructor($userID);
}

Аналіз:

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

Прийом конструювання:

Функція __constructor() вживається для ініціалізації об’єкту, коли нам потрібно завантажити ACL. Вона автоматично іменується при виклику new ACL();. Потім їй передається одиничний додатковий аргумент користувача, для якого завантажується ACL. Всередині конструктора ми перевіряємо, чи був переданий ID користувача. Якщо ніякого ID передано не було, припускаємо, що ACL буде завантажуватися для зареєстрованого користувача; так що змінної сеансу ми про це прочитаємо. В іншому випадку, якщо ми передамо ID користувача, це дозволить нам читати і редагувати ACL для іншого користувача, не того, який зареєструвався (нагоді для вашої сторінки адміністратора).

Після прочитання користувальницького ID ми викликаємо getUserRoles() для формування масиву рольових імен, привласнених користувачеві, і збереження його в змінній класу $userRoles. В кінці конструктора викликаємо buildACL() для створення поточного ACL. Метод під назвою ACL() – це опора для установок PHP4. При виклику new ACL() в PHP5 інтерпретатор PHP запускає метод __constructor(). Проте, коли ви виконуєте той самий код в PHP4, інтерпретатор запускає ACL(). Забезпечуючи метод, названий так само, як і клас, ми робимо клас PHP4 сумісним.

Кожного разу при створенні нового об’єкта ACL шляхом передачі ID користувача, цей об’єкт буде містити права доступу переданого користувача.

Допоміжні методи:

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

function getUserRoles()
{
$strSQL = «SELECT * FROM `user_roles` WHERE `userID` =» . floatval($this->userID) . «ORDER BY `addDate` ASC»;
$data = mysql_query($strSQL);
$resp = array();
while($row = mysql_fetch_array($data))
{
$resp[] = $row[‘roleID’];
}
return $resp;
}
function getAllRoles($format=’ids’)
{
$format = strtolower($format);
$strSQL = «SELECT * FROM `roles` ORDER BY `roleName` ASC»;
$data = mysql_query($strSQL);
$resp = array();
while($row = mysql_fetch_array($data))
{
if ($format == ‘full’)
{
$resp[] = array(«ID» => $row[‘ID’],»Name» => $row[‘roleName’]);
} else {
$resp[] = $row[‘ID’];
}
}
return $resp;
}
function buildACL()
{
//first, get the rules for the user’s role
if (count($this->userRoles) > 0)
{
$this->perms = array_merge($this->perms,$this->getRolePerms($this->userRoles));
}
//then, get the individual user permissions
$this->perms = array_merge($this->perms,$this->getUserPerms($this->userID));
}
function getPermKeyFromID($permID)
{
$strSQL = «SELECT `permKey` FROM `permissions` WHERE `ID` =» . floatval($permID) . «LIMIT 1»;
$data = mysql_query($strSQL);
$row = mysql_fetch_array($data);
return $row[0];
}
function getPermNameFromID($permID)
{
$strSQL = «SELECT `permName` FROM `permissions` WHERE `ID` =» . floatval($permID) . «LIMIT 1»;
$data = mysql_query($strSQL);
$row = mysql_fetch_array($data);
return $row[0];
}
function getRoleNameFromID($roleID)
{
$strSQL = «SELECT `roleName` FROM `roles` WHERE `ID` =» . floatval($roleID) . «LIMIT 1»;
$data = mysql_query($strSQL);
$row = mysql_fetch_array($data);
return $row[0];
}
function getUsername($userID)
{
$strSQL = «SELECT `username` FROM `users` WHERE `ID` =» . floatval($userID) . «LIMIT 1»;
$data = mysql_query($strSQL);
$row = mysql_fetch_array($data);
return $row[0];
}

getUserRoles()

getUserRoles() повертає масив рольових імен, призначених поточному користувачу. По-перше, ми побудуємо відповідний оператор SQL і виконаємо його. Користуючись while(), ми побудуємо з усіх відповідних результатів цикл і, нарешті, повернемо масив ID. Подібно до цього, getAllRoles() поверне всі доступні рольові імена (не тільки ті, які призначені користувачеві). Заснований на значенні аргументу, $format повертає масив ID всім рольовим іменами, або масив асоціативної матриці з ID ім’ям кожної ролі. Це дозволить нашій функції виконати подвійне навантаження. Якщо ми хочемо використовувати масив користувальницьких рольових імен в MySQL, то нам потрібний масив ID їх; а якщо ми хочемо показати рольові імена на своїй сторінці, було б корисно мати один масив з усією інформацією.

buildACL

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

Всі функції — getPermKeyFromID(), getPermNameFromID(), getRoleNameFromID() і getUsername() – є просто «довідковими» функціями. Вони дозволяють передати ID і повернути відповідне значення тексту. Видно, що ми будуємо оператор SQL, потім виконуємо його і повертаємо результат. Далі, додамо дві функції, які отримають з бази даних, права доступу.

function getRolePerms($role)
{
if (is_array($role))
{
$roleSQL = «SELECT * FROM `role_perms` WHERE `roleID` IN (» . implode(«,»,$role) . «) ORDER BY `ID` ASC»;
} else {
$roleSQL = «SELECT * FROM `role_perms` WHERE `roleID` =» . floatval($role) . «ORDER BY `ID` ASC»;
}
$data = mysql_query($roleSQL);
$perms = array();
while($row = mysql_fetch_assoc($data))
{
$pK = strtolower($this->getPermKeyFromID($row[‘permID’]));
if ($pK == «) { continue; }
if ($row[‘value’] === ‘1’) {
$hP = true;
} else {
$hP = false;
}
$perms[$pK] = array(‘perm’ => $pK,’inheritted’ => true,’value’ => $hP,’Name’ => $this->getPermNameFromID($row[‘permID’]),’ID’ => $row[‘permID’]);
}
return $perms;
}
function getUserPerms($userID)
{
$strSQL = «SELECT * FROM `user_perms` WHERE `userID` =» . floatval($userID) . «ORDER BY `addDate` ASC»;
$data = mysql_query($strSQL);
$perms = array();
while($row = mysql_fetch_assoc($data))
{
$pK = strtolower($this->getPermKeyFromID($row[‘permID’]));
if ($pK == «) { continue; }
if ($row[‘value’] == ‘1’) {
$hP = true;
} else {
$hP = false;
}
$perms[$pK] = array(‘perm’ => $pK,’inheritted’ => false,’value’ => $hP,’Name’ => $this->getPermNameFromID($row[‘permID’]),’ID’ => $row[‘permID’]);
}
return $perms;
}
function getAllPerms($format=’ids’)
{
$format = strtolower($format);
$strSQL = «SELECT * FROM `permissions` ORDER BY `permName` ASC»;
$data = mysql_query($strSQL);
$resp = array();
while($row = mysql_fetch_assoc($data))
{
if ($format == ‘full’)
{
$resp[$row[‘permKey’]] = array(‘ID’ => $row[‘ID’], ‘Name’ => $row[‘permName’], ‘Key’ => $row[‘permKey’]);
} else {
$resp[] = $row[‘ID’];
}
}
return $resp;
}

По суті, ці функції ідентичні, за винятком таблиць, з яких вони отримують інформацію. Єдиний аргумент – це ID для рольових імен користувачів, які потрібно вилучити. Функція рольових імен може бути передана масивом або цілим числом, в той час як функція користувача може бути передано лише цілим числом. Використовуючи is_array(), ми визначаємо, як трактувати аргумент функції права доступу. Якщо це масив, ми використовуємо implode() для створення списку, розділяється коми. В іншому випадку ми користуємося цим значенням в SQL. Потім створюємо новий порожній масив з назвою $perms – він буде зберігати права доступу в певному місці функції.

Всередині циклу while() ми виконуємо кілька функцій. Спочатку генеруємо змінну $pK, яку будемо використовувати в якості назви ключа масиву. Так як ми будемо шукати це значення, щоб визначити, чи має користувач спеціальне право доступу, важливо мати його в постійному форматі, ось чому ми користуємося strtolower(). Якщо ключове значення залишається порожнім, ми переходимо до чергового повтору за допомогою continue;. Далі, дивимося на $row[‘value’], щоб встановити неявне логічне значення для права доступу. Це гарантує, що тільки дійсне значення «1» у таблиці буде дорівнює true (тобто у користувача є право доступу), і важливо для безпеки. Інакше ми встановлюємо право доступу на false. Наприкінці функції створюємо масив з кількома поименованными ключами, так що можемо отримувати всю інформацію про права доступу. Цей масив присвоюється новому поименованному ключем у масиві $perms, який ми раніше створили. Зверніть увагу, що ми користуємося $pK для створення відповідно іменованого покажчика. Нарешті повертаємо масив.

Видно, що в повернутому масиві є назва покажчика «inherited» (успадкований). Для ACL він має спеціальне значення. Якщо користувач отримує право доступу тому, що воно належить рольовим імені, яке йому призначено, про нього говориться як про успадкованому. Якщо права доступу призначаються користувачеві вручну, вони не успадковуються.

У getAllPerms() ми споруджуємо список всіх доступних прав доступу. Подібно getAllRoles(), можна передати форматний аргумент для визначення того, яким чином будуть повернуті результати. А тепер про останній частині класу:

function userHasRole($roleID)
{
foreach($this->userRoles as $k => $v)
{
if (floatval($v) === floatval($roleID))
{
return true;
}
}
return false;
}
function hasPermission($permKey)
{
$permKey = strtolower($permKey);
if (array_key_exists($permKey,$this->perms))
{
if ($this->perms[$permKey][‘value’] === ‘1’ || $this->perms[$permKey][‘value’] === true)
{
return true;
} else {
return false;
}
} else {
return false;
}
}
}
?>

Останні два методи дуже важливі для функціональності ACL. userHasRole() приймає єдиний аргумент ID рольового імені. Рухаючись крізь всі елементи в масиві $userRoles, можна визначити, що визначено цим рольовому імені користувач. Якщо так, повертаємо true, якщо інакше — false. hasPermission() – це метод, який використовується нами для визначення, чи може користувач отримати доступ куди-небудь. Передаємо ключ для доступу, які потрібно перевірити. Ми робимо їх уніфікованими, перетворюючи в нижній регістр, і дивимося, чи є в масиві $perms покажчик з таким ім’ям. Якщо є, переконуємося, що він встановлений на «1» і повертає true, якщо навпаки – повертаємо false. Це та функція, яку ми використовуємо, якщо захочемо зрозуміти, чи можна щось зробити.

Крок 4: користувач Admin

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

Створення списку контролю доступу (ACL), входу на сайт

Відкрийте /admin/users.php і додайте наступний код:

hasPermission(‘access_admin’) != true){ header(«location: ../index.php»);}
?>
ACL Test

Main Screen | Home Admin

Як завжди, нам потрібно включити свою базу даних і файли ACL і встановити об’єкт ACL. Потім встановлюємо рівень безпеки сторінки. У цьому випадку ми гарантуємо, що у користувача є право доступу ‘access_admin’. Якщо ні, він спрямовується.

ПРИМІТКА: якщо змінити права доступу ACL таким чином, що у користувача № 1 не стане права доступу ‘access_admin’, ви не зможете увійти на сторону адміна. Також ви повинні спочатку зайти в /index.php до того, як відвідати яку-небудь із сторінок адміна, так як index.php встановлює змінну сеансу, визначаючи вас як userID #1.

Зараз це просто основна розмітка сторінки. В наступних кроках ми замінимо вищевказаний іншим кодом, що дозволить управляти користувачами. Ми споживемо змінну рядка запиту $action, щоб визначити, який з інтерфейсів потрібно відобразити. Можемо звернутися до чотирьох можливих значень: якщо це нуль, ми відображаємо список поточних користувачів. Якщо значення встановлено на ‘user’, відображаємо бланк окремого користувача. Якщо воно встановлено на ‘roles’, ми відображаємо бланк присвоєння імен користувачеві. Якщо встановлено на ‘perms’, показуємо бланк прав доступу, даних користувачеві.

Користувачі списку:

Вставте цей код у div з id ‘page’:

Select a User to Manage:

«;
}
} ?>

Ідея досить проста. Ми будуємо запит SQL, запускаємо його і переглядаємо результати. Для кожного користувача генеруємо посилання, яка допоможе редагувати цього конкретного користувача.

Редагуємо окремого користувача:

Тепер додайте цей код прямо під попереднім блоком програми:

Managing getUsername($_GET[‘userID’]); ?>:

… Some form to edit user info …

Roles for user: (Manage Roles)

    getUserRoles();
    foreach ($roles as $k => $v)
    {
    echo «

  • «. $userACL->getRoleNameFromID($v) . «
  • «;
    }
    ?>

Permissions for user: (Manage Permissions)

    perms;
    foreach ($perms as $k => $v)
    {
    if ($v[‘value’] === false) { continue; }
    echo «

  • «. $v[‘Name’];
    if ($v[‘inheritted’]) { echo » (inheritted)»; }
    echo «
  • «;
    }
    ?>

При редагуванні користувача потрібно завантажити для нього ACL. Це дозволить бачити, які у нього є рольові імена і права доступу. Починаємо зі створення нового об’єкта ACL і передачі $userID з рядка запиту (таким чином ми завантажуємо ACL цього користувача замість зареєстрованого користувача). Саме туди піде згодом ваша звичайна форма редагування користувача. Текстові поля для редагування імені користувача, пароля і ін. будуть звичними. Нижче ми розміщуємо список рольових імен, призначених користувачеві, а також забезпечуємо посилання, так що можна буде призначати користувачу та інші рольові імена. Рядки 10-16 завантажують всі рольові імена, призначені і друкують їх як елементи списку за допомогою foreach(). Потім ми в єдиному стилі вносимо в список права доступу користувача. Друкуємо тільки ті права доступу, які є у користувача, а не ті, які встановлені на false.

Створення списку контролю доступу (ACL), входу на сайт

Визначення рольових імен:

Бланк визначення рольових імен буде в підсумку виглядати таким чином:

Створення списку контролю доступу (ACL), входу на сайт

Внесіть цей код нижче попереднього блоку програми:

Manage User Roles: (getUsername($_GET[‘userID’]); ?>)

getAllRoles(‘full’);
foreach ($roles as $k => $v)
{
echo «

«;
echo «

«;
echo «

«;
echo «

«;
}
?>

Member Not Member
«. $v[‘Name’] . « userHasRole($v[‘ID’])) { echo » checked=\»checked\»»; }
echo » />
userHasRole($v[‘ID’])) { echo » checked=\»checked\»»; }
echo » />


Перше, що тут потрібно зробити – створити бланк і таблицю. У таблиці буде три колонки: одна для рольового імені, одна — для клітини з відповіддю учасника і одна — для клітини з відповіддю не учасника. Після створення нового об’єкта ACL завантажуємо масив всіх рольових імен за допомогою getAllRoles(). Це дозволить нам показувати вхідні елементи кожного рольового імені, а не тільки тих, які визначені користувачеві.

Всередині циклу foreach() робимо наступне: починаємо новий ряд і друкуємо позначку з назвою рольового імені. Потім друкуємо вхід кнопки з залежною фіксацією. Ім’я та id кнопок з залежною фіксацією робляться унікальними для кожного рольового імені за допомогою формату «role_[roleID]» (тобто role_0012). Рядка 13 і 16 визначають, яку з кнопок слід перевірити. Перша буде перевірена, якщо вже призначена група, в той час як друга – якщо ще немає. Зверніть увагу, що одна має значення «1» (для призначення), а інша – «0» (не призначення). Потім закінчуємо ряд.

Після всього цього додаємо деякі приховані елементи, які говорять нам, що ми зберігаємо і ID якого користувача потрібно зберегти. Потім додаємо клавіші submit (відправити) і cancel (скасувати).

Призначаємо права доступу:

Форма призначення прав доступу така ж, як бланк рольових імен, але з різними входами, так що давайте додамо цей код:

Manage User Permissions: (getUsername($_GET[‘userID’]); ?>)

perms;
$aPerms = $userACL->getAllPerms(‘full’);
foreach ($aPerms as $k => $v)
{
echo «

«;
echo «

«;
}
?>

«. $v[‘Name’] . « «;
echo «Allow»;
echo «Deny»;
echo «Inherit $iVal»;
echo «


Як у випадку з бланками рольових імен, починаємо з додавання форми і таблиці, на цей раз з двома колонками. Потім створюємо об’єкт ACL, витягаємо масив прав доступу (рядок 8) і отримуємо масив всіх прав доступу (рядок 9). У циклі foreach() друкуємо новий ряд і назву права доступу. Потім запускаємо елемент select (зазначити, вибрати). Біля входу select буде три варіанти вибору: Allow (дозволити), Заперечувати (відмовити) і Inherit (успадкувати). Дивимося на значення $rPerms[$v[‘Key’]][‘value’], щоб визначити, який з варіантів обрати. Дозволити або Заборонити не будуть обрані завдяки $rPerms[$v[‘Key’]][‘inheritted’] != true, якщо значення прав доступу успадковано. Якщо право доступу успадковано, буде обраний варіант Inherited.

Рядок 23-32 удосконалює варіант inherit. Якщо право доступу успадковано, вона робить його обраним. Потім визначає значення успадкованого права доступу і встановлює змінну $iVal таким чином, що ми можемо використовувати текстове значення в мітці опції у рядку 33. Після закінчення роботи над входом select і таблицею додаємо приховані входи для встановлення опцій збереження і додаємо кнопки submit і cancel.

Коли код запущено, все закінчується поруч для кожного наявного права доступу і переглядом, який показує, чи є вона у користувача.

Створення списку контролю доступу (ACL), входу на сайт

Збереження даних:

Додайте цей код у /admin/users.php прямо над тегом типу документа:

$v)
{
if (substr($k,0,5) == «role_»)
{
$roleID = str_replace(«role_»,»»,$k);
if ($v == ‘0’ || $v == ‘x’) {
$strSQL = sprintf(«DELETE FROM `user_roles` WHERE `userID` = %u AND `roleID` = %u»,$_POST[‘userID’],$roleID);
} else {
$strSQL = sprintf(«REPLACE INTO `user_roles` SET `userID` = %u, `roleID` = %u, `addDate` = ‘%s'»,$_POST[‘userID’],$roleID,date («Y-m-d H:i:s»));
}
mysql_query($strSQL);
}
}
break;
case ‘savePerms’:
$redir = «?action=user&userID=» . $_POST[‘userID’];
foreach ($_POST as $k => $v)
{
if (substr($k,0,5) == «perm_»)
{
$permID = str_replace(«perm_»,»»,$k);
if ($v == ‘x’)
{
$strSQL = sprintf(«DELETE FROM `user_perms` WHERE `userID` = %u AND `permID` = %u»,$_POST[‘userID’],$permID);
} else {
$strSQL = sprintf(«REPLACE INTO `user_perms` SET `userID` = %u, `permID` = %u, `value` = %u, `addDate` = ‘%s'»,$_POST[‘userID’],$permID,$v,date («Y-m-d H:i:s»));
}
mysql_query($strSQL);
}
}
break;
}
header(«location: users.php» . $redir);
}
?>

Цей код спочатку перевіряє, чи було щось призначено, шляхом перегляду $_POST[‘action’]. Це – значення, яке містилося в одному з елементів прихованої форми тих двох бланків, які ми зробили.

Якщо ми призначили тільки бланк рольових імен, відбувається наступне:

Ми будуємо рядок запитів $redir, до якої будемо відіслані після обробки бланка.

Переглядаємо цикл з усіх змінних $_POST.

За допомогою substr() шукаємо, є перші 5 символів імені змінної «role_». Таким чином ми отримуємо входи прав доступу для наступних кроків.

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

Якщо значення «0» або «х» (рядок 14), виконуємо заміну запиту.

Для будь-якого запиту використовуємо sprintf() в цілях забезпечення безпеки (sprintf() запускає введення змінних і допомагає захистити інформацію від атак на SQL).

Запускаємо SQL за допомогою mysql_query().

Зверніть увагу на заміну запиту: синтаксис заміни – це спеціальний синтаксис MySQL, що дозволяє плавне оновлення або вставку. Використовуючи заміну, можна позбутися від написання великої кількості коду PHP. Роблячи таблицю user_roles ми створили унікальний покажчик в полях userID і roleID. При запуску оператора ‘replace into’ він спочатку дивиться в таблиці, створить внесення нового ряду копію (тобто ряд з тими ж значеннями покажчика вже існує). Якщо існує ряд, який відповідає вказівниками, оператор доповнює цей ряд. Якщо ні, він вставляє новий ряд.

Якщо ми просто відправили бланк прав доступу, відбувається те ж саме, за винятком того, що відбувається пошук відрізняється приставки в назві входу, а також використовується інша таблиця бази даних. Як тільки зроблені які-небудь дії, використовуємо header(«location:…») для перенаправлення назад на ту сторінку, на якій ми вже були, і приєднуємо готову змінну рядка запиту $redir.

Крок 5: рольові імена адміністратора

Тепер, коли закінчена робота над бланками управління користувачами, потрібно управляти своїми рольовими іменами. Щоб було простіше, слід зробити всього дві дії: переглянути список рольових імен або відредагувати рольове ім’я. Створіть /admin/roles.php за допомогою наступного коду:

hasPermission(‘access_admin’) != true){ header(«location: ../index.php»);}
?>
ACL Test

Main Screen | Home Admin

Складаємо список рольових імен:

Так само, як на користувача сторінці, ми починаємо з включень, створюючи об’єкт ACL, і з формату сторінки. Наша дія за замовчуванням (сторінка завантажена без рядка запиту) – скласти список доступних рольових імен, тому вставте цей код :

Select a Role to Manage:

getAllRoles(‘full’);
foreach ($roles as $k => $v)
{
echo «» . $v[‘Name’] . «
«;
}
if (count($roles) < 1)
{
echo «No roles yet.
«;
} ?>

Спочатку перевіряємо, чи порожня мінлива рядка запиту. Потім зберігаємо список всіх доступних рольових імен в $roles за допомогою getAllRoles(). При кожному повторенні циклу foreach() робимо посилання, яка приведе нас до форми редагування кожного рольового імені окремо. Якщо в масиві $roles немає рольових імен, показуємо доброзичливе послання. Нарешті, додаємо кнопку, яка дозволить нам додати нове рольове ім’я.

Редагуємо рольове ім’я:

New Role:

Manage Role: (getRoleNameFromID($_GET[‘roleID’]); ?>)

Name:

getRolePerms($_GET[‘roleID’]);
$aPerms = $myACL->getAllPerms(‘full’);
foreach ($aPerms as $k => $v)
{
echo «

«;
echo «

«;
echo «

«;
echo «

«;
echo «

«;
}
?>

Allow Deny Ignore
«. $v[‘Name’] . «


Після перевірки, чи знаходиться там мінлива рядка запиту, дивимося, чи була передана в рядок запиту roleID. Якщо була, припускаємо, що ми редагуємо рольове ім’я, якщо немає – створюємо його (відображаємо заголовок як належить). Потім створюємо бланк. Всередині нього нам потрібен текстовий вхід для імені ролі і таблиця, що містить права доступу. В таблиці є колонки для назви права доступу, а також allow (дозволити), заперечувати (заборонити) і ignore (ігнорувати). Як і під час редагування прав доступу користувачів, нам потрібно переглянути масив всіх прав доступу (рядок 15, $myACL->getAllPerms(‘full’)).

У кожному ряду друкуємо назву права доступу і три кнопки з залежною фіксацією. Вони використовують ту ж саму систему позначень, що і користувальницька форма («perm_[permID]»). «Allow (дозволити) або «Заборонити» (заборонити) обрані в залежності від значення збереженого права доступу (завдяки рядків 19 і 22). Якщо вибрати «ignore» (ігнорувати), для поєднання цих рольового імені/права доступу не зберігається ніякого значення. Зверніть увагу, що перші два блоку if() містять у собі && $_GET[‘roleID’] != «. Це гарантує, що якщо не передано жодного ID користувача (що ми створюємо нове рольове ім’я), «ignore» вибирається за замовчуванням. Потім додаємо приховані входи для встановлення налаштувань збереження і закриваємо бланк. Таким же чином додаємо інший бланк з прихованими входами, щоб видалити рольове ім’я, і ще один бланк з кнопкою скасування «cancel», яка буде повертати нас на сторінку рольових імен. Якщо все йде за планом, при спробі редагування прав доступу для рольового імені ми повинні отримати наступне:

Створення списку контролю доступу (ACL), входу на сайт

Збереження даних:

Вставте цей код у /admin/roles.php прямо перед тегом типу документа:

1)
{
$roleID = $_POST[‘roleID’];
} else {
$roleID = mysql_insert_id();
}
foreach ($_POST as $k => $v)
{
if (substr($k,0,5) == «perm_»)
{
$permID = str_replace(«perm_»,»»,$k);
if ($v == ‘X’)
{
$strSQL = sprintf(«DELETE FROM `role_perms` WHERE `roleID` = %u AND `permID` = %u»,$roleID,$permID);
mysql_query($strSQL);
continue;
}
$strSQL = sprintf(«REPLACE INTO `role_perms` SET `roleID` = %u, `permID` = %u, `value` = %u, `addDate` = ‘%s'»,$roleID,$permID,$v,date («Y-m-d H:i:s»));
mysql_query($strSQL);
}
}
header(«location: roles.php»);
break;
case ‘delRole’:
$strSQL = sprintf(«DELETE FROM `roles` WHERE `ID` = %u LIMIT 1»,$_POST[‘roleID’]);
mysql_query($strSQL);
$strSQL = sprintf(«DELETE FROM `user_roles` WHERE `roleID` = %u»,$_POST[‘roleID’]);
mysql_query($strSQL);
$strSQL = sprintf(«DELETE FROM `role_perms` WHERE `roleID` = %u»,$_POST[‘roleID’]);
mysql_query($strSQL);
header(«location: roles.php»);
break;
}
}
?>

Аналогічно користувача сторінці перевіряємо, чи було що-то відправлено через $_POST і яке було значення $_POST[‘action’]. Якщо б ми зберігали рольове ім’я, то зробили б наступне:

Заміну запиту в таблиці рольових імен. Вона оновить/додасть рольове ім’я. Рядки 8-13 виконують важливу функцію у збереженні рольових імен. Якщо робити оновлення, то у нас вже є ID для цього рольового імені. Однак якщо потрібно додати, ID рольового імені ми не знаємо. При виконанні заміни запиту повертається кількість задіяних рядів. Якщо їх було більше одного, ряд оновлюється, так що нам потрібно використовувати id рольового імені з бланка. Якщо було задіяно не більше одного ряду, він вставляється, а ми користуємося mysql_insert_id() для отримання ID останнього доданого ряду.

Потім переглядаємо змінні $_POST, і рядок 16 гарантує, що нами виконуються ряди, де назва входу починається з «perm_».

Рядок 18 отримує floatval() права доступу, таким чином, в результаті ми маємо тільки ціле значення ID «perm» (так ми довідаємося, з яким із прав доступу ми зараз маємо справу).

if ($v == ‘x’) {…} запуститься, якщо в бланку вибрати для права доступу «Ignore». Вона спробує видалити ряд з таблиці, в якому вірні ID ряду і ID права доступу. Якщо це трапиться, ми використовуємо continue; для переходу до наступної змінної.

Якщо ми добралися до цього моменту, припускаємо, що потрібно додати або оновити право доступу для цього рольового імені. Так, ми використовуємо синтаксис «replace into», який використовували у формі користувача. Важливо мати в ньому roleID і permID, щоб база даних могла коригувати існуючий ряд.

Нарешті запускаємо SQL і переходимо на сторінку рольових імен.

Якщо ми відправляємо форму вилучення, то видаляємо рольове ім’я таблиці рольових імен. Потім таким же чином видаляємо будь-які записи з таблиць user_roles і role_perms, які збігаються з ID рольового імені, так що нам не доведеться в підсумку мати справу з користувачами і правами доступу, призначеними неіснуючим рольовим іменами. Тепер переходимо до сторінки рольових імен.

Крок 6: права доступу адміністратора

Як у рольових імен адміністратора, так і у його прав доступу є дві функції: скласти список наявних прав доступу і редагувати їх. Почнемо з цього коду /admin/perms.php:

hasPermission(‘access_admin’) != true){ header(«location: ../index.php»);}
?>
ACL Test

Main Screen | Home Admin

Складаємо список прав доступу:

Помістіть цей код у div е сторінки (на місці ):

Select a Permission to Manage:

getAllPerms(‘full’);
foreach ($roles as $k => $v)
{
echo «» . $v[‘Name’] . «
«;
}
if (count($roles) < 1)
{
echo «No permissions yet.
«;
} ?>

Спочатку використовуємо getAllPerms() для отримання масиву всіх прав доступу. Потім переглянемо їх все для побудови свого списку. Кожне повторення циклу foreach() сформує посилання, яка буде направляти нас на сторінку для редагування даного права доступу. Якщо прав доступу в наявності немає, ми показуємо це повідомленням і закінчуємо форму кнопкою «New Permission» (нове право доступу).

Ось результат:

Створення списку контролю доступу (ACL), входу на сайт

Редагуємо право доступу:

Щоб редагувати/додати індивідуальне право доступу, потрібно вставити цей код прямо за попереднім блоком:

New Permission:

Manage Permission: (getPermNameFromID($_GET[‘permID’]); ?>)

Name:
Key:

Як у бланках рольових імен, перевіряємо, чи передбачено в рядку запиту ID права доступу і відображаємо доповнення, або оновлюємо заснований на нього заголовок. Відкриваємо тег форми і додаємо два текстових входу: один для назви права доступу, другий для ключа права доступу. Назва з’явиться в бланках, в той час як ключ – це те, що нами використовувалося в скриптах. Ключ повинен сильно нагадувати назва, за винятком того, що в ньому не повинно бути пробілів і символів, і повинен використовуватися нижній регістр. Обидва текстових поля ми забезпечимо значеннями за промовчанням на випадок оновлень.

У кінці форми додаємо приховані входи і кнопку «надіслати». Потім робимо бланки delete (видалити) і «cancel» (скасувати).

Збереження даних:

Нарешті нам потрібно зберегти бланк прав доступу, так що додайте цей код нагору /admin/perms.php прямо над типом документа.

if (isset($_POST[‘action’]))
{
switch($_POST[‘action’])
{
case ‘savePerm’:
$strSQL = sprintf(«REPLACE INTO `permissions` SET `ID` = %u, `permName` = ‘%s’, `permKey` = ‘%s'»,$_POST[‘permID’],$_POST[‘permName’],$_POST[‘permKey’]);
mysql_query($strSQL);
break;
case ‘delPerm’:
$strSQL = sprintf(«DELETE FROM `permissions` WHERE `ID` = %u LIMIT 1»,$_POST[‘permID’]);
mysql_query($strSQL);
break;
}
header(«location: perms.php»);
}

Як у всіх інших скриптах прав доступу, нам потрібно з’ясувати, яку дію було відправлено. Якщо ми зберігаємо право доступу, то виконуємо над дією заміну. Вона або оновити, або вставить відповідність. Якщо ми відправили форму вилучення, то виконуємо запит на видалення. В іншому випадку, нас перенаправляють на perms.php.

Крок 7: ядро адміністратора

Нам необхідна початкова точка ACL admin. Просто створимо щось просте з посиланнями на три сторінки. Ось превью (попередній перегляд) і код:

Створення списку контролю доступу (ACL), входу на сайт

hasPermission(‘access_admin’) != true){ header(«location: ../index.php»);}
?>
ACL Test

Main Screen

Select an Admin Function:

Manage Users
Manage Roles
Manage Permissions

Все досить зрозуміло — у нас три посилання для управління трьома різними аспектами свого ACL.

Крок 8: запуск ACL на вашому сайті

Створення списку контролю доступу (ACL), входу на сайт

Застосувати нову систему ACL на своєму сайті досить просто. Кожна сторінка, яку потрібно захистити, повинна мати базу даних і файл ACL, вставлений вгорі. Після того потрібно створити новий екземпляр об’єкта ACL.

Наприклад, у вас встановлено право доступу з ключем «access_admin» і ви хотіли б використовувати його, щоб контролювати доступ до інтерфейсу адміна. Вгорі сторінки можна було б використовувати для перевірки цей скрипт:

hasPermission(‘access_admin’) != true)
{
header(«location: insufficientPermission.php»);
}
?>

Бачите, ми створили об’єкт ACL. Так як ми не передаємо ID користувача в якості аргументу, система прочитає змінну сеансу $_SESSION[‘userID’]. Потім використовуємо $myACL->hasPermission(‘access_admin’) для перевірки, чи є у користувача це право доступу. Якщо ні, він спрямовується на insufficientPermission.php. Таким чином, він не може увійти в захищені галузі, які не має права доступу.

В наданому вихідному файлі я передбачив файл покажчика, який забезпечує просте тестування ACL, засноване на вищенаведеному прикладі коду. Зразок покажчика демонструє список всіх прав доступу та іконки, які показують, чи може цей користувач отримати до кожного з них доступ. Також тут є список користувачів, який дозволяє замінити користувача, для чого і показується ACL. Ось код для зразка покажчика:

ACL Test

Admin Screen

Permissions for getUsername($userID); ?>:

getAllPerms(‘full’);
foreach ($aPerms as $k => $v)
{
echo «» . $v[‘Name’] . «: «;
echo «Створення списку контролю доступу (ACL), входу на сайтhasPermission($v[‘Key’]) === true)
{
echo «allow.png»;
$pVal = «Allow»;
} else {
echo «deny.png»;
$pVal = «Deny»;
}
echo «\» width=\»16\» height=\»16\» alt=\»$pVal\» />
«;
}
?>

Change User:

«;
}
?>

Заключні думки

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

Сподіваюся, урок по створенню списку контролю доступу для сайту, який вам сподобався.

Переклад і редакція: Ріг Віктор і Андрій Бернацький. Команда webformyself.

E-mail: [email protected]

Проект webformyself.com — Як створити свій сайт. Основи самостійного сайтобудування

«Кіберсант-вебмастер» — самий повний курс по сайтостроению в рунеті!

P. S. Хочете опублікувати цікавий тематичний матеріал і заробити? Якщо відповідь «Так», то тисніть сюди.