Това е част 5 от поредица за това как да създадете система за управление на потребителски акаунти в PHP. Можете да намерите другите части тук:част 1, част 2, част 3 и част 4.
Завършихме управлението на административните потребителски акаунти в последния раздел, както и ролите. В тази част ще преминем през създаване на разрешения и присвояване и премахване на разрешенията на потребителски роли.
Задаване на разрешения на роли
Както казах в първата част на тази поредица, ролите са свързани с разрешения във връзка „Много към много“. Една роля може да има много разрешения и едно разрешение може да принадлежи на много роли.
Вече създадохме таблицата с ролите. Сега ще създадем таблица с разрешения за съхраняване на разрешения и трета таблица, наречена permission_role, за да съхранява информацията за връзката между ролите и таблицата с разрешения.
Създайте двете таблици, за да имат следните свойства:
таблица с разрешения:
CREATE TABLE `permissions` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255) NOT NULL UNIQUE KEY,
`description` text NOT NULL
)
таблица с ролеви_разрешения:
CREATE TABLE `permission_role` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`role_id` int(11) NOT NULL,
`permission_id` int(11) NOT NULL,
KEY `role_id` (`role_id`),
KEY `permission_id` (`permission_id`),
CONSTRAINT `permission_role_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `permission_role_ibfk_2` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`)
)
В таблицата permission_role, role_id препраща към идентификатора на ролята в таблицата с роли, докато permission_id препраща към колоната на permission id в таблицата с разрешения. За да присвоим разрешение на роля, ние правим това, като просто вмъкваме запис на този permission_id срещу role_id в таблицата permission_role и връзката се установява. Това означава, че ако искаме да премахнем това разрешение от тази роля, просто изтриваме записа на този role_id срещу този permission_id в тази таблица permission_role.
Последните два реда от SQL заявката по-горе са ограничения, които гарантират, че когато конкретна роля или разрешение бъдат изтрити, всички записи в таблицата permission_role, които имат идентификатора на това разрешение или този идентификатор на ролята, също ще бъдат автоматично изтрити от базата данни. Правим това, защото не искаме таблицата permission_role да съхранява информация за връзката за роля или разрешение, които вече не съществуват.
Можете също да зададете тези ограничения ръчно, като използвате PHPMyAdmin:можете да го направите в интерфейса, просто като изберете таблицата permission_role и отидете в раздела Релационен изглед> Структура и просто попълните стойностите. Ако все още не можете да направите това, оставете коментар по-долу и аз ще се опитам да ви помогна.
Сега връзката е установена.
Нека създадем страница, за да зададем разрешения на роля. На нашата страница roleList.php ние изброяваме различните роли с бутон „разрешения“ до всяка. Щракването върху тази връзка ще ни отведе до страница, наречена assignPermissions.php. Нека създадем този файл сега в папката admin/roles.
assignPermissions.php:
<?php include('../../config.php') ?>
<?php include(ROOT_PATH . '/admin/roles/roleLogic.php') ?>
<?php
$permissions = getAllPermissions();
if (isset($_GET['assign_permissions'])) {
$role_id = $_GET['assign_permissions']; // The ID of the role whose permissions we are changing
$role_permissions = getRoleAllPermissions($role_id); // Getting all permissions belonging to role
// array of permissions id belonging to the role
$r_permissions_id = array_column($role_permissions, "id");
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Admin Area - Assign permissions </title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
<!-- Custome styles -->
<link rel="stylesheet" href="../../static/css/style.css">
</head>
<body>
<?php include(INCLUDE_PATH . "/layouts/admin_navbar.php") ?>
<div class="col-md-4 col-md-offset-4">
<a href="roleList.php" class="btn btn-success">
<span class="glyphicon glyphicon-chevron-left"></span>
Roles
</a>
<hr>
<h1 class="text-center">Assign permissions</h1>
<br />
<?php if (count($permissions) > 0): ?>
<form action="assignPermissions.php" method="post">
<table class="table table-bordered">
<thead>
<tr>
<th>N</th>
<th>Role name</th>
<th class="text-center">Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($permissions as $key => $value): ?>
<tr class="text-center">
<td><?php echo $key + 1; ?></td>
<td><?php echo $value['name']; ?></td>
<td>
<input type="hidden" name="role_id" value="<?php echo $role_id; ?>">
<!-- if current permission id is inside role's ids, then check it as already belonging to role -->
<?php if (in_array($value['id'], $r_permissions_id)): ?>
<input type="checkbox" name="permission[]" value="<?php echo $value['id'] ?>" checked>
<?php else: ?>
<input type="checkbox" name="permission[]" value="<?php echo $value['id'] ?>" >
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<tr>
<td colspan="3">
<button type="submit" name="save_permissions" class="btn btn-block btn-success">Save permissions</button>
</td>
</tr>
</tbody>
</table>
</form>
<?php else: ?>
<h2 class="text-center">No permissions in database</h2>
<?php endif; ?>
</div>
<?php include(INCLUDE_PATH . "/layouts/footer.php") ?>
</body>
</html>
Щракването върху бутона „разрешения“ на дадена роля ви отвежда до тази страница. Но точно сега, ако щракнете върху него и стигнете до тази страница assignPermissions.php, има съобщение за грешка, че функцията getAllPermissions() е недефинирана. Преди да добавим този метод, нека разгледаме как всъщност внедряваме това присвояване и премахване на разрешение в нашия PHP код.
Когато щракнете върху бутона „разрешения“ на роля, ще бъдете отведени до страницата assignPermissions.php с идентификатора на тази роля. Но преди да покажем страницата за присвояване на разрешения, ние използваме идентификатора на ролята, за да извлечем всички разрешения, които вече са били присвоени на тази роля от базата данни. И тогава ние също така извеждаме всички разрешения, налични в таблицата с разрешения. Сега имаме два масива от разрешения:тези, които са присвоени на роля и всички разрешения, налични в нашата база данни. Едното първо е подмножество на второто.
Присвояването на разрешение на роля означава добавяне на това разрешение от общия списък с разрешения към масива от разрешения, принадлежащи на тази роля, и запазване на цялата тази информация в таблицата permission_role. Отмяната на присвояване на разрешение от роля означава премахване на това конкретно разрешение от списъка с разрешения, които принадлежат на тази роля.
Нашата логика е да преминем през масив от всички налични разрешения от базата данни, след което за всеки техен идентификатор ние определяме дали този идентификатор вече е в масива от идентификатори за разрешенията на ролята. ако съществува, това означава, че ролята вече има това разрешение, така че ние го показваме заедно с отметнато квадратче. Ако не съществува, ние го показваме до неотметнато квадратче.
След като всичко е показано, щракването върху отметнато квадратче означава отмяна на присвояването на разрешението, докато щракването върху неотметнато квадратче означава присвояване на разрешението на ролята. След като всички проверки и премахване са направени, потребителят щраква върху бутона „Запазване на разрешенията“ под таблицата, за да запази всички разрешения, които са били проверени за тази роля.
Нека добавим всички тези функции към файла roleLogic.php. Те са:
roleLogic.php:
// ... other functions up here ...
function getAllPermissions(){
global $conn;
$sql = "SELECT * FROM permissions";
$permissions = getMultipleRecords($sql);
return $permissions;
}
function getRoleAllPermissions($role_id){
global $conn;
$sql = "SELECT permissions.* FROM permissions
JOIN permission_role
ON permissions.id = permission_role.permission_id
WHERE permission_role.role_id=?";
$permissions = getMultipleRecords($sql, 'i', [$role_id]);
return $permissions;
}
function saveRolePermissions($permission_ids, $role_id) {
global $conn;
$sql = "DELETE FROM permission_role WHERE role_id=?";
$result = modifyRecord($sql, 'i', [$role_id]);
if ($result) {
foreach ($permission_ids as $id) {
$sql_2 = "INSERT INTO permission_role SET role_id=?, permission_id=?";
modifyRecord($sql_2, 'ii', [$role_id, $id]);
}
}
$_SESSION['success_msg'] = "Permissions saved";
header("location: roleList.php");
exit(0);
}
Поставяне на разрешенията за работа
В този момент можем да определим каква е ролята на потребителя и тъй като ролята е свързана с разрешения, следователно можем да знаем и техните разрешения.
Сега искаме да задействаме тези разрешения: тоест да гарантираме, че на потребител с администратор е разрешено да извършва само онези действия, за които има разрешенията. Ще постигнем това с помощта на мидълуерни функции. По същество междинният софтуер е част от код или функция, която се изпълнява преди да бъде извършено действие. Обикновено тази функция на междинния софтуер може да промени поведението на действието или да извърши някои проверки, които в крайна сметка могат да спрат действието напълно в зависимост от резултатите от проверката.
Например, потребителят може да има разрешения за създаване-публикация, актуализиране-публикуване и изтриване-публикация. Ако те са влезли и се опитат да публикуват публикация, нашата функция за междинен софтуер първо проверява дали този потребител има разрешение за публикуване. Ако имат това разрешение, нашата функция за междинен софтуер ще върне true и публикацията ще бъде публикувана. Ако им липсва разрешение за публикуване на публикация, нашата функция за междинен софтуер ще ги пренасочи обратно със съобщение, че нямат разрешение да публикуват публикацията.
Ще поставим всички наши функции за междинен софтуер в един файл, наречен Middleware.php. Създайте го сега в папката admin и поставете този код в него:
Middleware.php:
<?php
// if user is NOT logged in, redirect them to login page
if (!isset($_SESSION['user'])) {
header("location: " . BASE_URL . "login.php");
}
// if user is logged in and this user is NOT an admin user, redirect them to landing page
if (isset($_SESSION['user']) && is_null($_SESSION['user']['role'])) {
header("location: " . BASE_URL);
}
// checks if logged in admin user can update post
function canUpdatePost($post_id = null){
global $conn;
if(in_array('update-post', $_SESSION['userPermissions'])){
if ($_SESSION['user']['role'] === "Author") { // author can update only posts that they themselves created
$sql = "SELECT user_id FROM posts WHERE id=?";
$post_result = getSingleRecord($sql, 'i', [$post_id]);
$post_user_id = $post_result['user_id'];
// if current user is the author of the post, then they can update the post
if ($post_user_id === $user_id) {
return true;
} else { // if post is not created by this author
return false;
}
} else { // if user is not author
return true;
}
} else {
return false;
}
}
// accepts user id and post id and checks if user can publis/unpublish a post
function canPublishPost() {
if(in_array(['permission_name' => 'publish-post'], $_SESSION['userPermissions'])){
// echo "<pre>"; print_r($_SESSION['userPermissions']); echo "</pre>"; die();
return true;
} else {
return false;
}
}
function canDeletePost() {
if(in_array('delete-post', $_SESSION['userPermissions'])){
return true;
} else {
return false;
}
}
function canCreateUser() {
if(in_array('create-user', $_SESSION['userPermissions'])){
return true;
} else {
return false;
}
}
function canUpdateUser() {
if(in_array('update-user', $_SESSION['userPermissions'])){
return true;
} else {
return false;
}
}
function canDeleteUser() {
if(in_array('delete-user', $_SESSION['userPermissions'])){
return true;
} else {
return false;
}
}
function canCreateRole($role_id) {
if(in_array('create-role', $_SESSION['userPermissions'])){
return true;
} else {
return false;
}
}
function canUpdateRole($role_id) {
if(in_array('update-role', $_SESSION['userPermissions'])){
return true;
} else {
return false;
}
}
function canDeleteRole($user_id, $post_id) {
if(in_array('delete-role', $_SESSION['userPermissions'])){
return true;
} else {
return false;
}
}
?>
"; умре (); върне истина; } else { return false; } } function canDeletePost() { if(in_array('delete-post', $_SESSION['userPermissions'])){ return true; } else { return false; } } function canCreateUser() { if(in_array('create-user', $_SESSION['userPermissions'])){ return true; } else { return false; } } function canUpdateUser() { if(in_array('update-user', $_SESSION['userPermissions'])){ return true; } else { return false; } } function canDeleteUser() { if(in_array('delete-user', $_SESSION['userPermissions'])){ return true; } else { return false; } } function canCreateRole($role_id) { if(in_array('create-role', $_SESSION['userPermissions'])){ return true; } else { return false; } } function canUpdateRole($role_id) { if(in_array('update-role', $_SESSION['userPermissions'])){ return true; } else { return false; } } function canDeleteRole($user_id, $post_id) { if(in_array('delete-role', $_SESSION['userPermissions'])){ return true; } else { return false; } }?> Първият if оператор проверява дали потребителят е влязъл. Ако потребителят не е влязъл, той ще бъде пренасочен към началната страница. Вторият оператор if проверява дали потребителят е влязъл и дали има роля (е администратор). Ако се установи, че потребителят е влязъл и има роля, той ще получи достъп до страницата, в противен случай ще бъде пренасочен обратно към началната страница.
Във всеки файл, където искате да ограничите достъпа на потребители без администратор, трябва просто да включите този файл Middleware.php в този файл. Така че всички наши администраторски файлове, до които не искаме да имат достъп нормалните потребители, ще включим този файл в тях. Така че отворете всички файлове в двете папки в администраторската папка, а именно:потребители, роли. Във всеки от файловете добавете следния ред точно под включването за config.php.
Така:
<?php include('../../config.php'); ?>
<?php require_once '../middleware.php'; ?>
И това ще пренасочи всеки потребител без администратор, който се опитва да посети страницата.
Също така в този файл Middleware.php използваме in_array() на PHP, за да проверим дали разрешението, което тестваме, е в масива от разрешенията на този потребител. (Когато администратор на потребител влезе, ние поставяме всичките му разрешения в масив от променливи на сесия, наречен $_SESSION['userPermissions'].) Ако текущото разрешение е в масива от разрешения на потребителя, това означава, че този потребител има това разрешение и следователно функцията връща true, в противен случай връща false.
Сега, ако искате да проверите дали даден потребител има разрешение, кажете, че разрешението за публикуване, което трябва да направите, е да извикате метода canPublishPost() по следния начин:
<?php if (canPublishPost()): ?>
<!-- User can publish post. Display publish post button -->
<?php else: ?>
<!-- User cannot publish post. Do not display publish post button -->
<?php endif ?>
Също като междинен софтуер, преди да актуализираме публикация, първо ще извикаме функцията за междинен софтуер canUpdatePost(). Ако функцията провери и види, че потребителят няма разрешение за актуализиране на публикация, тя ще върне false и след това можем да го пренасочим към началната страница със съобщение, че не му е разрешено да актуализира публикацията. Като това:
// checks if logged in admin user can update post
function updatePost($post_values){
global $conn;
if(canUpdatePost($post_values['id']){
// proceed to update post
} else {
// redirect back to homepage with message that says user is not permitted to update post
}
}
Същото нещо за публикуване/отмяна на публикации:
function togglePublishPost($post_id)
{
if (!canPublishPost($_SESSION['user']['id'])) {
// redirect them back to dashboard with the message that they don't have the permission to publish post
}
// proceed to publish post
}
Сега ни остава последната част от този урок, която актуализира потребителския профил и също така предоставя на регистрираните потребители възможността да изтриват собствените си акаунти.
Благодаря, че следвате. Ако имате нещо да кажете, моля, напишете го в коментарите.