Toto je 5. část série o tom, jak vytvořit systém správy uživatelských účtů v PHP. Ostatní části naleznete zde:část 1, část 2, část 3 a část 4.
V poslední části jsme dokončili správu administrativních uživatelských účtů a rolí. V této části projdeme vytvořením oprávnění a přidělováním a rušením přidělování oprávnění uživatelským rolím.
Přidělování oprávnění rolím
Jak jsem řekl v první části této série, role souvisí s oprávněními ve vztahu Many-To-Many. Role může mít mnoho oprávnění a oprávnění může patřit mnoha rolím.
Již jsme vytvořili tabulku rolí. Nyní vytvoříme tabulku oprávnění pro uchovávání oprávnění a třetí tabulku nazvanou permit_role pro uchovávání informací o vztahu mezi rolemi a tabulkou oprávnění.
Vytvořte dvě tabulky tak, aby měly následující vlastnosti:
tabulka oprávnění:
CREATE TABLE `permissions` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(255) NOT NULL UNIQUE KEY,
`description` text NOT NULL
)
tabulka oprávnění_role:
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`)
)
V tabulce permit_role odkazuje role_id na ID role v tabulce rolí, zatímco permit_id odkazuje na sloupec ID oprávnění v tabulce oprávnění. Chcete-li přiřadit oprávnění k roli, provedeme to jednoduše vložením záznamu tohoto permit_id do role_id v tabulce permit_role a vztah je vytvořen. To znamená, že pokud chceme zrušit přiřazení tohoto oprávnění k této roli, pouze smažeme záznam tohoto id role proti tomuto id_povolení v této tabulce permit_role.
Poslední dva řádky výše uvedeného dotazu SQL jsou omezení, která zajišťují, že při smazání konkrétní role nebo oprávnění budou databáze automaticky smazány také všechny položky v tabulce permit_role s id daného oprávnění nebo tohoto id role. Děláme to, protože nechceme, aby tabulka permit_role uchovávala informace o vztahu o roli nebo o oprávnění, které již neexistuje.
Tato omezení můžete také nastavit ručně pomocí PHPMyAdmin:můžete to udělat na rozhraní jednoduše tak, že vyberete tabulku permit_role a přejdete na Relational view> tab Structure a pouze vyplníte hodnoty. Pokud to stále nemůžete udělat, zanechte níže komentář a já se vám pokusím pomoci.
Nyní je vztah nastaven.
Pojďme vytvořit stránku pro přiřazení oprávnění k roli. Na naší stránce roleList.php uvádíme různé role s tlačítkem 'oprávnění' vedle každé. Kliknutím na tento odkaz se dostaneme na stránku s názvem assignPermissions.php. Vytvořme tento soubor nyní ve složce 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>
Kliknutím na tlačítko 'oprávnění' role se dostanete na tuto stránku. Ale právě teď, když na něj kliknete a přejdete na tuto stránku assignPermissions.php, zobrazí se chybová zpráva, že funkce getAllPermissions() nejsou definovány. Než přidáme tuto metodu, pojďme si projít, jak vlastně implementujeme toto přidělování a zrušení přiřazení oprávnění v našem kódu PHP.
Když kliknete na tlačítko 'oprávnění' role, budete přesměrováni na stránku assignPermissions.php s ID této role. Před zobrazením stránky přiřazení oprávnění však pomocí ID role načteme z databáze všechna oprávnění, která již byla dané roli přiřazena. A pak také zobrazíme všechna oprávnění dostupná v tabulce oprávnění. Nyní máme dvě pole oprávnění:ta, která byla přiřazena k roli a všechna oprávnění dostupná v naší databázi. Jeden první je podmnožinou druhého.
Přiřazení oprávnění roli znamená přidání tohoto oprávnění z celkového seznamu oprávnění do pole oprávnění náležejících k dané roli a uložení všech těchto informací do tabulky permit_role. Zrušení přiřazení oprávnění roli znamená odebrání konkrétního oprávnění ze seznamu oprávnění, která k dané roli patří.
Naší logikou je procházet polem všech dostupných oprávnění z databáze a poté pro každé jejich id určíme, zda je toto id již v poli id oprávnění dané role. pokud existuje, znamená to, že role již toto oprávnění má, takže je zobrazíme vedle zaškrtnutého políčka. Pokud neexistuje, zobrazíme jej vedle nezaškrtnutého políčka.
Poté, co je vše zobrazeno, kliknutí na zaškrtnuté políčko znamená zrušení přidělení oprávnění, zatímco kliknutí na nezaškrtnuté políčko znamená přidělení oprávnění roli. Po dokončení všech zaškrtnutí a zrušení zaškrtnutí uživatel klikne na tlačítko 'Uložit oprávnění' pod tabulkou, aby se všechna oprávnění, která byla zaškrtnuta pro danou roli, uloží.
Přidejme všechny tyto funkce do souboru roleLogic.php. Jsou to:
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);
}
Uvedení oprávnění do provozu
V tomto okamžiku můžeme určit, jaká je role uživatele, a protože role souvisí s oprávněními, můžeme znát i jejich oprávnění.
Nyní chceme tato oprávnění zprovoznit: to znamená zajistit, aby uživatel správce mohl provádět pouze ty akce, ke kterým má oprávnění. Toho docílíme pomocí middlewarových funkcí. Middleware je v podstatě část kódu nebo funkce, která se provede před provedením akce. Tato funkce middlewaru může obvykle upravit chování akce nebo provést některé kontroly, které mohou v závislosti na výsledcích kontroly skončit akci úplně.
Uživatel může mít například oprávnění vytvořit-příspěvek, aktualizovat-příspěvek a odstranit-příspěvek. Pokud jsou přihlášeni a pokusí se publikovat příspěvek, naše funkce middlewaru nejprve zkontroluje, zda má tento uživatel oprávnění k publikování příspěvku. Pokud mají toto oprávnění, naše funkce middlewaru vrátí hodnotu true a příspěvek bude zveřejněn. Pokud jim chybí oprávnění k publikování příspěvku, naše funkce middlewaru je přesměruje zpět se zprávou, že nemají oprávnění k publikování příspěvku.
Všechny naše middlewarové funkce vložíme do jednoho souboru s názvem middleware.php. Vytvořte jej nyní ve složce admin a vložte do něj tento kód:
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;
}
}
?>
"; zemřít(); vrátit 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; } }?> První příkaz if kontroluje, zda je uživatel přihlášen. Pokud uživatel přihlášen není, bude přesměrován na domovskou stránku. Druhý příkaz if kontroluje, zda je uživatel přihlášen a zda má roli (je admin). Pokud se zjistí, že uživatel je přihlášený a má roli, vstoupí na stránku, jinak bude přesměrován zpět na domovskou stránku.
V každém souboru, ke kterému chcete omezit přístup neadministrátorů, byste měli do tohoto souboru zahrnout pouze tento soubor middleware.php. Takže všechny naše admin soubory, ke kterým nechceme mít přístup normální uživatelé, do nich zahrneme tento soubor. Otevřete tedy všechny soubory ve dvou složkách ve složce admin, jmenovitě:uživatelé, role. Do každého ze souborů přidejte následující řádek těsně pod include pro config.php.
Asi takhle:
<?php include('../../config.php'); ?>
<?php require_once '../middleware.php'; ?>
A to přesměruje každého uživatele, který není správcem, při pokusu o návštěvu stránky.
Také v tomto souboru middleware.php používáme PHP in_array() ke kontrole, zda je oprávnění, které testujeme, v poli oprávnění daného uživatele. (Když se uživatel admin přihlásí, vložíme všechna jeho oprávnění do pole proměnných relace s názvem $_SESSION['userPermissions'].) Pokud je aktuální oprávnění v poli uživatelských oprávnění, znamená to, že daný uživatel má toto oprávnění a proto funkce vrací true, jinak vrací false.
Nyní, pokud chcete zkontrolovat, zda má uživatel oprávnění, řekněme, že oprávnění publikovat-příspěvek, které musíte udělat, je zavolat metodu canPublishPost() takto:
<?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 ?>
Také jako middleware, než aktualizujeme příspěvek, nejprve zavoláme funkci middlewaru canUpdatePost(). Pokud funkce zkontroluje a uvidí, že uživatel nemá oprávnění k aktualizaci příspěvku, vrátí hodnotu false a my jej pak můžeme přesměrovat na domovskou stránku se zprávou, že nemá oprávnění příspěvek aktualizovat. Takhle:
// 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
}
}
Totéž pro publikování/rušení publikování příspěvků:
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
}
Nyní nám zbývá poslední část tohoto tutoriálu, která aktualizuje uživatelský profil a také dává registrovaným uživatelům možnost smazat své vlastní účty.
Děkujeme za sledování. Pokud máte co říct, napište to prosím do komentářů.