Added permissions system.

This commit is contained in:
flash 2018-07-08 21:24:59 +02:00
parent ffc23c11fd
commit f924484ac3
16 changed files with 929 additions and 289 deletions

View file

@ -0,0 +1,54 @@
.permissions {
display: flex;
flex-direction: column;
margin-bottom: 4px;
&__line {
display: flex;
font-size: 1.1em;
line-height: 1.4em;
&--header {
font-size: 1.2em;
line-height: 1.5em;
border-bottom: 1px solid #333;
padding-bottom: 1px;
&:not(:first-child) {
margin-top: 4px;
}
}
}
&__title {
flex: 1 1 auto;
padding: 4px;
}
&__input {
cursor: pointer;
}
&__choice {
width: 100px;
text-align: center;
padding: 4px;
&--radio {
cursor: pointer;
border-left: 1px solid #333;
}
&--yes:hover {
background-color: #0a0;
}
&--no:hover {
background-color: #a00;
}
&--never:hover {
background-color: #400;
}
}
}

View file

@ -30,6 +30,7 @@ body {
@import "classes/pagination";
@import "classes/user-listing";
@import "classes/permissions";
@import "classes/changelog-change";
@import "classes/changelog-tags";

View file

@ -0,0 +1,39 @@
<?php
namespace Misuzu\DatabaseMigrations\InitialPermissionsTable;
use PDO;
function migrate_up(PDO $conn): void
{
// if you need new permission sets, create a migration that adds a new column to this table.
$conn->exec("
CREATE TABLE `msz_permissions` (
`user_id` INT(10) UNSIGNED NULL DEFAULT NULL,
`role_id` INT(10) UNSIGNED NULL DEFAULT NULL,
`user_perms_allow` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`user_perms_deny` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`changelog_perms_allow` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`changelog_perms_deny` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`news_perms_allow` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`news_perms_deny` INT(10) UNSIGNED NOT NULL DEFAULT '0',
UNIQUE INDEX `user_id` (`user_id`),
UNIQUE INDEX `role_id` (`role_id`),
CONSTRAINT `role_id_foreign`
FOREIGN KEY (`role_id`)
REFERENCES `msz_roles` (`role_id`)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT `user_id_foreign`
FOREIGN KEY (`user_id`)
REFERENCES `msz_users` (`user_id`)
ON UPDATE CASCADE
ON DELETE CASCADE
)
");
}
function migrate_down(PDO $conn): void
{
$conn->exec('DROP TABLE `msz_permissions`');
}

View file

@ -7,6 +7,7 @@ require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/src/changelog.php';
require_once __DIR__ . '/src/colour.php';
require_once __DIR__ . '/src/manage.php';
require_once __DIR__ . '/src/news.php';
require_once __DIR__ . '/src/perms.php';
require_once __DIR__ . '/src/zalgo.php';
require_once __DIR__ . '/src/Forum/forum.php';
@ -82,7 +83,7 @@ if (PHP_SAPI !== 'cli') {
}
$inManageMode = starts_with($_SERVER['REQUEST_URI'], '/manage');
$hasManageAccess = perms_check(perms_get_user(MSZ_PERMS_USER, $app->getUserId()), MSZ_PERM_MANAGE);
$hasManageAccess = perms_check(perms_get_user(MSZ_PERMS_USER, $app->getUserId()), MSZ_USER_PERM_CAN_MANAGE);
$tpl->var('has_manage_access', $hasManageAccess);
if ($inManageMode) {

View file

@ -12,7 +12,7 @@ $queryOffset = (int)($_GET['o'] ?? 0);
switch ($_GET['v'] ?? null) {
case 'changes':
if (!perms_check($changelogPerms, MSZ_CHANGELOG_MANAGE_CHANGES)) {
if (!perms_check($changelogPerms, MSZ_CHANGELOG_PERM_MANAGE_CHANGES)) {
echo render_error(403);
break;
}
@ -67,7 +67,7 @@ switch ($_GET['v'] ?? null) {
break;
case 'change':
if (!perms_check($changelogPerms, MSZ_CHANGELOG_MANAGE_CHANGES)) {
if (!perms_check($changelogPerms, MSZ_CHANGELOG_PERM_MANAGE_CHANGES)) {
echo render_error(403);
break;
}
@ -191,7 +191,7 @@ switch ($_GET['v'] ?? null) {
break;
case 'tags':
if (!perms_check($changelogPerms, MSZ_CHANGELOG_MANAGE_TAGS)) {
if (!perms_check($changelogPerms, MSZ_CHANGELOG_PERM_MANAGE_TAGS)) {
echo render_error(403);
break;
}
@ -228,7 +228,7 @@ switch ($_GET['v'] ?? null) {
break;
case 'tag':
if (!perms_check($changelogPerms, MSZ_CHANGELOG_MANAGE_TAGS)) {
if (!perms_check($changelogPerms, MSZ_CHANGELOG_PERM_MANAGE_TAGS)) {
echo render_error(403);
break;
}
@ -289,7 +289,7 @@ switch ($_GET['v'] ?? null) {
break;
case 'actions':
if (!perms_check($changelogPerms, MSZ_CHANGELOG_MANAGE_ACTIONS)) {
if (!perms_check($changelogPerms, MSZ_CHANGELOG_PERM_MANAGE_ACTIONS)) {
echo render_error(403);
break;
}
@ -326,7 +326,7 @@ switch ($_GET['v'] ?? null) {
break;
case 'action':
if (!perms_check($changelogPerms, MSZ_CHANGELOG_MANAGE_ACTIONS)) {
if (!perms_check($changelogPerms, MSZ_CHANGELOG_PERM_MANAGE_ACTIONS)) {
echo render_error(403);
break;
}

View file

@ -4,16 +4,22 @@ use Misuzu\Database;
require_once __DIR__ . '/../../misuzu.php';
$db = Database::connection();
$templating = $app->getTemplating();
$tpl = $app->getTemplating();
$userPerms = perms_get_user(MSZ_PERMS_USER, $app->getUserId());
$isPostRequest = $_SERVER['REQUEST_METHOD'] === 'POST';
$queryQffset = (int)($_GET['o'] ?? 0);
$tpl->vars([
'can_manage_users' => $canManageUsers = perms_check($userPerms, MSZ_USER_PERM_MANAGE_USERS),
'can_manage_roles' => $canManageRoles = perms_check($userPerms, MSZ_USER_PERM_MANAGE_ROLES),
'can_manage_perms' => $canManagePerms = perms_check($userPerms, MSZ_USER_PERM_MANAGE_PERMS),
]);
switch ($_GET['v'] ?? null) {
case 'listing':
if (!perms_check($userPerms, MSZ_PERM_MANAGE_USERS)) {
if (!$canManageUsers && !$canManagePerms) {
echo render_error(403);
break;
}
@ -38,17 +44,17 @@ switch ($_GET['v'] ?? null) {
$getManageUsers->bindValue('take', $usersTake);
$manageUsers = $getManageUsers->execute() ? $getManageUsers->fetchAll() : [];
$templating->vars([
$tpl->vars([
'manage_users' => $manageUsers,
'manage_users_count' => $manageUsersCount,
'manage_users_range' => $usersTake,
'manage_users_offset' => $queryQffset,
]);
echo $templating->render('@manage.users.listing');
echo $tpl->render('@manage.users.listing');
break;
case 'view':
if (!perms_check($userPerms, MSZ_PERM_MANAGE_USERS)) {
if (!$canManageUsers && !$canManagePerms) {
echo render_error(403);
break;
}
@ -104,22 +110,62 @@ switch ($_GET['v'] ?? null) {
$getAvailableRoles->bindValue('user_id', $manageUser['user_id']);
$availableRoles = $getAvailableRoles->execute() ? $getAvailableRoles->fetchAll() : [];
if ($canManagePerms) {
$tpl->var('permissions', $permissions = manage_perms_list(perms_get_user_raw($userId)));
}
if ($isPostRequest) {
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
echo 'csrf err';
break;
}
if (isset($_POST['avatar'])) {
switch ($_POST['avatar']['mode'] ?? '') {
case 'delete':
user_avatar_delete($manageUser['user_id']);
break;
case 'upload':
user_avatar_set_from_path($manageUser['user_id'], $_FILES['avatar']['tmp_name']['file']);
break;
if (!empty($_POST['user']) && is_array($_POST['user'])
&& user_validate_username($_POST['user']['username']) === ''
&& user_validate_email($_POST['user']['email']) === '') {
$updateUserDetails = $db->prepare('
UPDATE `msz_users`
SET `username` = :username,
`email` = LOWER(:email),
`user_title` = :title
WHERE `user_id` = :user_id
');
$updateUserDetails->bindValue('username', $_POST['user']['username']);
$updateUserDetails->bindValue('email', $_POST['user']['email']);
$updateUserDetails->bindValue(
'title',
strlen($_POST['user']['title'])
? $_POST['user']['title']
: null
);
$updateUserDetails->bindValue('user_id', $userId);
$updateUserDetails->execute();
}
if (!empty($_POST['avatar']) && !empty($_POST['avatar']['delete'])) {
user_avatar_delete($manageUser['user_id']);
} elseif (!empty($_FILES['avatar'])) {
user_avatar_set_from_path($manageUser['user_id'], $_FILES['avatar']['tmp_name']['file']);
}
if (!empty($_POST['password'])
&& is_array($_POST['password'])
&& !empty($_POST['password']['new'])
&& !empty($_POST['password']['confirm'])
&& user_validate_password($_POST['password']['new']) === ''
&& $_POST['password']['new'] === $_POST['password']['confirm']) {
$updatePassword = $db->prepare('
UPDATE `msz_users`
SET `password` = :password
WHERE `user_id` = :user_id
');
$updatePassword->bindValue('password', user_password_hash($_POST['password']['new']));
$updatePassword->bindValue('user_id', $userId);
$updatePassword->execute();
}
if (!empty($_POST['profile']) && is_array($_POST['profile'])) {
user_profile_fields_set($userId, $_POST['profile']);
}
if (isset($_POST['add_role'])) {
@ -140,26 +186,55 @@ switch ($_GET['v'] ?? null) {
}
}
if (!empty($permissions) && !empty($_POST['perms']) && is_array($_POST['perms'])) {
$perms = manage_perms_apply($permissions, $_POST['perms']);
if ($perms !== null) {
$permKeys = array_keys($perms);
$setPermissions = $db->prepare('
REPLACE INTO `msz_permissions`
(`role_id`, `user_id`, `' . implode('`, `', $permKeys) . '`)
VALUES
(NULL, :user_id, :' . implode(', :', $permKeys) . ')
');
$setPermissions->bindValue('user_id', $userId);
foreach ($perms as $key => $value) {
$setPermissions->bindValue($key, $value);
}
$setPermissions->execute();
} else {
$deletePermissions = $db->prepare('
DELETE FROM `msz_permissions`
WHERE `role_id` IS NULL
AND `user_id` = :user_id
');
$deletePermissions->bindValue('user_id', $userId);
$deletePermissions->execute();
}
}
header("Location: ?v=view&u={$manageUser['user_id']}");
break;
}
$templating->vars([
$tpl->vars([
'available_roles' => $availableRoles,
'has_roles' => $hasRoles,
'view_user' => $manageUser,
'profile_fields' => user_profile_fields_get(),
]);
echo $templating->render('@manage.users.view');
echo $tpl->render('@manage.users.view');
break;
case 'roles':
if (!perms_check($userPerms, MSZ_PERM_MANAGE_ROLES)) {
if (!$canManageRoles && !$canManagePerms) {
echo render_error(403);
break;
}
$rolesTake = 10;
$manageRolesCount = $db->query('
SELECT COUNT(`role_id`)
FROM `msz_roles`
@ -180,23 +255,27 @@ switch ($_GET['v'] ?? null) {
$getManageRoles->bindValue('take', $rolesTake);
$manageRoles = $getManageRoles->execute() ? $getManageRoles->fetchAll() : [];
$templating->vars([
$tpl->vars([
'manage_roles' => $manageRoles,
'manage_roles_count' => $manageRolesCount,
'manage_roles_range' => $rolesTake,
'manage_roles_offset' => $queryQffset,
]);
echo $templating->render('@manage.users.roles');
echo $tpl->render('@manage.users.roles');
break;
case 'role':
if (!perms_check($userPerms, MSZ_PERM_MANAGE_ROLES)) {
if (!$canManageRoles && !$canManagePerms) {
echo render_error(403);
break;
}
$roleId = $_GET['r'] ?? null;
if ($canManagePerms) {
$tpl->var('permissions', $permissions = manage_perms_list(perms_get_role_raw($roleId)));
}
if ($isPostRequest) {
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
echo 'csrf err';
@ -243,28 +322,53 @@ switch ($_GET['v'] ?? null) {
}
}
$roleDescription = $_POST['role']['description'] ?? '';
$roleDescription = $_POST['role']['description'] ?? null;
$roleTitle = $_POST['role']['title'] ?? null;
if (strlen($roleDescription) > 1000) {
if ($roleDescription !== null) {
$rdLength = strlen($roleDescription);
if ($rdLength < 1) {
$roleDescription = null;
} elseif ($rdLength > 1000) {
echo 'description is too long';
break;
}
}
if ($roleTitle !== null) {
$rtLength = strlen($roleTitle);
if ($rtLength < 1) {
$roleTitle = null;
} elseif ($rtLength > 64) {
echo 'title is too long';
break;
}
}
if ($roleId < 1) {
$updateRole = $db->prepare('
INSERT INTO `msz_roles`
(`role_name`, `role_hierarchy`, `role_secret`, `role_colour`, `role_description`, `created_at`)
(
`role_name`, `role_hierarchy`, `role_secret`, `role_colour`,
`role_description`, `created_at`, `role_title`
)
VALUES
(:role_name, :role_hierarchy, :role_secret, :role_colour, :role_description, NOW())
(
:role_name, :role_hierarchy, :role_secret, :role_colour,
:role_description, NOW(), :role_title
)
');
} else {
$updateRole = $db->prepare('
UPDATE `msz_roles` SET
`role_name` = :role_name,
UPDATE `msz_roles`
SET `role_name` = :role_name,
`role_hierarchy` = :role_hierarchy,
`role_secret` = :role_secret,
`role_colour` = :role_colour,
`role_description` = :role_description
`role_description` = :role_description,
`role_title` = :role_title
WHERE `role_id` = :role_id
');
$updateRole->bindValue('role_id', $roleId);
@ -275,12 +379,42 @@ switch ($_GET['v'] ?? null) {
$updateRole->bindValue('role_secret', $roleSecret ? 1 : 0);
$updateRole->bindValue('role_colour', $roleColour);
$updateRole->bindValue('role_description', $roleDescription);
$updateRole->bindValue('role_title', $roleTitle);
$updateRole->execute();
if ($roleId < 1) {
$roleId = (int)$db->lastInsertId();
}
if (!empty($permissions) && !empty($_POST['perms']) && is_array($_POST['perms'])) {
$perms = manage_perms_apply($permissions, $_POST['perms']);
if ($perms !== null) {
$permKeys = array_keys($perms);
$setPermissions = $db->prepare('
REPLACE INTO `msz_permissions`
(`role_id`, `user_id`, `' . implode('`, `', $permKeys) . '`)
VALUES
(:role_id, NULL, :' . implode(', :', $permKeys) . ')
');
$setPermissions->bindValue('role_id', $roleId);
foreach ($perms as $key => $value) {
$setPermissions->bindValue($key, $value);
}
$setPermissions->execute();
} else {
$deletePermissions = $db->prepare('
DELETE FROM `msz_permissions`
WHERE `role_id` = :role_id
AND `user_id` IS NULL
');
$deletePermissions->bindValue('role_id', $roleId);
$deletePermissions->execute();
}
}
header("Location: ?v=role&r={$roleId}");
break;
}
@ -304,9 +438,9 @@ switch ($_GET['v'] ?? null) {
break;
}
$templating->vars(['edit_role' => $editRole]);
$tpl->vars(['edit_role' => $editRole]);
}
echo $templating->render('@manage.users.roles_create');
echo $tpl->render('@manage.users.roles_create');
break;
}

View file

@ -15,11 +15,11 @@ $userPerms = perms_get_user(MSZ_PERMS_USER, $app->getUserId());
$settingsModes = [
'account' => [
'title' => 'Account',
'allow' => perms_check($userPerms, MSZ_PERM_EDIT_PROFILE),
'allow' => perms_check($userPerms, MSZ_USER_PERM_EDIT_PROFILE),
],
'avatar' => [
'title' => 'Avatar',
'allow' => perms_check($userPerms, MSZ_PERM_CHANGE_AVATAR),
'allow' => perms_check($userPerms, MSZ_USER_PERM_CHANGE_AVATAR),
],
'sessions' => [
'title' => 'Sessions',

View file

@ -3,16 +3,16 @@ use Misuzu\Application;
use Misuzu\Database;
use Misuzu\IO\File;
define('MSZ_PERM_EDIT_PROFILE', 1);
define('MSZ_PERM_CHANGE_AVATAR', 1 << 1);
define('MSZ_USER_PERM_EDIT_PROFILE', 1);
define('MSZ_USER_PERM_CHANGE_AVATAR', 1 << 1);
define('MSZ_PERM_MANAGE', 1 << 20);
define('MSZ_PERM_MANAGE_USERS', 1 << 21);
define('MSZ_PERM_MANAGE_ROLES', 1 << 22);
define('MSZ_PERM_MANAGE_PERMS', 1 << 23);
define('MSZ_PERM_MANAGE_REPORTS', 1 << 24);
define('MSZ_PERM_MANAGE_RESTRICTIONS', 1 << 25);
define('MSZ_PERM_MANAGE_BLACKLISTS', 1 << 26);
define('MSZ_USER_PERM_CAN_MANAGE', 1 << 19);
define('MSZ_USER_PERM_MANAGE_USERS', 1 << 20);
define('MSZ_USER_PERM_MANAGE_ROLES', 1 << 21);
define('MSZ_USER_PERM_MANAGE_PERMS', 1 << 22);
define('MSZ_USER_PERM_MANAGE_REPORTS', 1 << 23);
define('MSZ_USER_PERM_MANAGE_RESTRICTIONS', 1 << 24);
define('MSZ_USER_PERM_MANAGE_BLACKLISTS', 1 << 25);
define('MSZ_USERS_PASSWORD_HASH_ALGO', PASSWORD_ARGON2I);

View file

@ -1,9 +1,12 @@
<?php
use Misuzu\Database;
define('MSZ_CHANGELOG_MANAGE_CHANGES', 1);
define('MSZ_CHANGELOG_MANAGE_TAGS', 1 << 1);
define('MSZ_CHANGELOG_MANAGE_ACTIONS', 1 << 2);
define('MSZ_CHANGELOG_PERM_MANAGE_CHANGES', 1);
define('MSZ_CHANGELOG_PERM_MANAGE_TAGS', 1 << 1);
define('MSZ_CHANGELOG_PERM_MANAGE_ACTIONS', 1 << 2);
define('MSZ_CHANGELOG_PERM_DELETE_COMMENTS', 1 << 3);
define('MSZ_CHANGELOG_PERM_EDIT_COMMENTS', 1 << 4);
define('MSZ_CHANGELOG_PERM_PIN_COMMENTS', 1 << 5);
function changelog_action_add(string $name, ?int $colour = null, ?string $class = null): int
{

View file

@ -3,7 +3,7 @@ function manage_get_menu(int $userId): array
{
$userPerms = perms_get_user(MSZ_PERMS_USER, $userId);
if (!perms_check($userPerms, MSZ_PERM_MANAGE)) {
if (!perms_check($userPerms, MSZ_USER_PERM_CAN_MANAGE)) {
return [];
}
@ -19,33 +19,25 @@ function manage_get_menu(int $userId): array
'Settings' => '/manage/index.php?v=settings',
];
$canUsers = perms_check($userPerms, MSZ_PERM_MANAGE_USERS);
$canRoles = perms_check($userPerms, MSZ_PERM_MANAGE_ROLES);
$canPerms = perms_check($userPerms, MSZ_PERM_MANAGE_PERMS);
$canReports = perms_check($userPerms, MSZ_PERM_MANAGE_REPORTS);
$canRestricts = perms_check($userPerms, MSZ_PERM_MANAGE_RESTRICTIONS);
$canBlacklists = perms_check($userPerms, MSZ_PERM_MANAGE_BLACKLISTS);
$canUsers = perms_check($userPerms, MSZ_USER_PERM_MANAGE_USERS);
$canRoles = perms_check($userPerms, MSZ_USER_PERM_MANAGE_ROLES);
$canPerms = perms_check($userPerms, MSZ_USER_PERM_MANAGE_PERMS);
$canReports = perms_check($userPerms, MSZ_USER_PERM_MANAGE_REPORTS);
$canRestricts = perms_check($userPerms, MSZ_USER_PERM_MANAGE_RESTRICTIONS);
$canBlacklists = perms_check($userPerms, MSZ_USER_PERM_MANAGE_BLACKLISTS);
if ($canUsers || $canRoles || $canPerms
|| $canReports || $canRestricts || $canBlacklists) {
$menu['Users'] = [];
if ($canUsers) {
if ($canUsers || $canPerms) {
$menu['Users']['Listing'] = '/manage/users.php?v=listing';
}
if ($canRoles || $canPerms) {
$menu['Users'][] = '_';
if ($canRoles) {
$menu['Users']['Roles'] = '/manage/users.php?v=roles';
}
if ($canPerms) {
$menu['Users']['Permissions'] = '/manage/users.php?v=permissions';
}
}
if ($canReports || $canRestricts || $canBlacklists) {
$menu['Users'][] = '_';
@ -69,9 +61,9 @@ function manage_get_menu(int $userId): array
'Settings' => '/manage/forums.php?v=settings',
];*/
$canChanges = perms_check($changelogPerms, MSZ_CHANGELOG_MANAGE_CHANGES);
$canChangeTags = perms_check($changelogPerms, MSZ_CHANGELOG_MANAGE_TAGS);
$canChangeActions = perms_check($changelogPerms, MSZ_CHANGELOG_MANAGE_ACTIONS);
$canChanges = perms_check($changelogPerms, MSZ_CHANGELOG_PERM_MANAGE_CHANGES);
$canChangeTags = perms_check($changelogPerms, MSZ_CHANGELOG_PERM_MANAGE_TAGS);
$canChangeActions = perms_check($changelogPerms, MSZ_CHANGELOG_PERM_MANAGE_ACTIONS);
if ($canChanges || $canChangeTags || $canChangeActions) {
$menu['Changelog'] = [];
@ -91,3 +83,291 @@ function manage_get_menu(int $userId): array
return $menu;
}
function manage_perms_value(int $perm, int $allow, int $deny): string
{
if (perms_check($deny, $perm)) {
return 'never';
}
if (perms_check($allow, $perm)) {
return 'yes';
}
return 'no';
}
function manage_perms_apply(array $list, array $post): ?array
{
$perms = perms_create();
foreach ($list as $section) {
if (empty($post[$section['section']])
|| !is_array($post[$section['section']])) {
continue;
}
$allowKey = perms_get_key($section['section'], 'allow');
$denyKey = perms_get_key($section['section'], 'deny');
foreach ($section['perms'] as $perm) {
if (empty($post[$section['section']][$perm['section']])) {
continue;
}
switch ($post[$section['section']][$perm['section']]) {
case 'yes':
$perms[$allowKey] |= $perm['perm'];
$perms[$denyKey] &= ~$perm['perm'];
break;
case 'never':
$perms[$allowKey] &= ~$perm['perm'];
$perms[$denyKey] |= $perm['perm'];
break;
case 'no':
default:
$perms[$allowKey] &= ~$perm['perm'];
$perms[$denyKey] &= ~$perm['perm'];
break;
}
}
}
$returnNothing = 0;
foreach ($perms as $perm) {
$returnNothing |= $perm;
}
if ($returnNothing === 0) {
return null;
}
return $perms;
}
function manage_perms_list(array $rawPerms): array
{
return [
[
'section' => 'user',
'title' => 'User',
'perms' => [
[
'section' => 'edit-profile',
'title' => 'Can edit own profile.',
'perm' => MSZ_USER_PERM_EDIT_PROFILE,
'value' => manage_perms_value(
MSZ_USER_PERM_EDIT_PROFILE,
$rawPerms['user_perms_allow'],
$rawPerms['user_perms_deny']
),
],
[
'section' => 'change-avatar',
'title' => 'Can change own avatar.',
'perm' => MSZ_USER_PERM_CHANGE_AVATAR,
'value' => manage_perms_value(
MSZ_USER_PERM_CHANGE_AVATAR,
$rawPerms['user_perms_allow'],
$rawPerms['user_perms_deny']
),
],
[
'section' => 'can-manage',
'title' => 'Can access the management panel.',
'perm' => MSZ_USER_PERM_CAN_MANAGE,
'value' => manage_perms_value(
MSZ_USER_PERM_CAN_MANAGE,
$rawPerms['user_perms_allow'],
$rawPerms['user_perms_deny']
),
],
[
'section' => 'manage-users',
'title' => 'Can manage other users.',
'perm' => MSZ_USER_PERM_MANAGE_USERS,
'value' => manage_perms_value(
MSZ_USER_PERM_MANAGE_USERS,
$rawPerms['user_perms_allow'],
$rawPerms['user_perms_deny']
),
],
[
'section' => 'manage-roles',
'title' => 'Can manage roles.',
'perm' => MSZ_USER_PERM_MANAGE_ROLES,
'value' => manage_perms_value(
MSZ_USER_PERM_MANAGE_ROLES,
$rawPerms['user_perms_allow'],
$rawPerms['user_perms_deny']
),
],
[
'section' => 'manage-perms',
'title' => 'Can manage permissions.',
'perm' => MSZ_USER_PERM_MANAGE_PERMS,
'value' => manage_perms_value(
MSZ_USER_PERM_MANAGE_PERMS,
$rawPerms['user_perms_allow'],
$rawPerms['user_perms_deny']
),
],
[
'section' => 'manage-reports',
'title' => 'Can handle reports.',
'perm' => MSZ_USER_PERM_MANAGE_REPORTS,
'value' => manage_perms_value(
MSZ_USER_PERM_MANAGE_REPORTS,
$rawPerms['user_perms_allow'],
$rawPerms['user_perms_deny']
),
],
[
'section' => 'manage-restrictions',
'title' => 'Can manage restrictions.',
'perm' => MSZ_USER_PERM_MANAGE_RESTRICTIONS,
'value' => manage_perms_value(
MSZ_USER_PERM_MANAGE_RESTRICTIONS,
$rawPerms['user_perms_allow'],
$rawPerms['user_perms_deny']
),
],
[
'section' => 'manage-blacklistings',
'title' => 'Can manage blacklistings.',
'perm' => MSZ_USER_PERM_MANAGE_BLACKLISTS,
'value' => manage_perms_value(
MSZ_USER_PERM_MANAGE_BLACKLISTS,
$rawPerms['user_perms_allow'],
$rawPerms['user_perms_deny']
),
],
],
],
[
'section' => 'news',
'title' => 'News',
'perms' => [
[
'section' => 'manage-posts',
'title' => 'Can manage posts.',
'perm' => MSZ_NEWS_PERM_MANAGE_POSTS,
'value' => manage_perms_value(
MSZ_NEWS_PERM_MANAGE_POSTS,
$rawPerms['news_perms_allow'],
$rawPerms['news_perms_deny']
),
],
[
'section' => 'manage-cats',
'title' => 'Can manage catagories.',
'perm' => MSZ_NEWS_PERM_MANAGE_CATEGORIES,
'value' => manage_perms_value(
MSZ_NEWS_PERM_MANAGE_CATEGORIES,
$rawPerms['news_perms_allow'],
$rawPerms['news_perms_deny']
),
],
[
'section' => 'comments-delete',
'title' => 'Can delete comments from others.',
'perm' => MSZ_NEWS_PERM_DELETE_COMMENTS,
'value' => manage_perms_value(
MSZ_NEWS_PERM_DELETE_COMMENTS,
$rawPerms['news_perms_allow'],
$rawPerms['news_perms_deny']
),
],
[
'section' => 'comments-edit',
'title' => 'Can edit comments from others.',
'perm' => MSZ_NEWS_PERM_EDIT_COMMENTS,
'value' => manage_perms_value(
MSZ_NEWS_PERM_EDIT_COMMENTS,
$rawPerms['news_perms_allow'],
$rawPerms['news_perms_deny']
),
],
[
'section' => 'comments-pin',
'title' => 'Can pin comments.',
'perm' => MSZ_NEWS_PERM_PIN_COMMENTS,
'value' => manage_perms_value(
MSZ_NEWS_PERM_PIN_COMMENTS,
$rawPerms['news_perms_allow'],
$rawPerms['news_perms_deny']
),
],
],
],
[
'section' => 'changelog',
'title' => 'Changelog',
'perms' => [
[
'section' => 'manage-changes',
'title' => 'Can manage changes.',
'perm' => MSZ_CHANGELOG_PERM_MANAGE_CHANGES,
'value' => manage_perms_value(
MSZ_CHANGELOG_PERM_MANAGE_CHANGES,
$rawPerms['changelog_perms_allow'],
$rawPerms['changelog_perms_deny']
),
],
[
'section' => 'manage-tags',
'title' => 'Can manage tags.',
'perm' => MSZ_CHANGELOG_PERM_MANAGE_TAGS,
'value' => manage_perms_value(
MSZ_CHANGELOG_PERM_MANAGE_TAGS,
$rawPerms['changelog_perms_allow'],
$rawPerms['changelog_perms_deny']
),
],
[
'section' => 'manage-actions',
'title' => 'Can manage action types.',
'perm' => MSZ_CHANGELOG_PERM_MANAGE_ACTIONS,
'value' => manage_perms_value(
MSZ_CHANGELOG_PERM_MANAGE_ACTIONS,
$rawPerms['changelog_perms_allow'],
$rawPerms['changelog_perms_deny']
),
],
[
'section' => 'comments-delete',
'title' => 'Can delete comments from others.',
'perm' => MSZ_CHANGELOG_PERM_DELETE_COMMENTS,
'value' => manage_perms_value(
MSZ_CHANGELOG_PERM_DELETE_COMMENTS,
$rawPerms['changelog_perms_allow'],
$rawPerms['changelog_perms_deny']
),
],
[
'section' => 'comments-edit',
'title' => 'Can edit comments from others.',
'perm' => MSZ_CHANGELOG_PERM_EDIT_COMMENTS,
'value' => manage_perms_value(
MSZ_CHANGELOG_PERM_EDIT_COMMENTS,
$rawPerms['changelog_perms_allow'],
$rawPerms['changelog_perms_deny']
),
],
[
'section' => 'comments-pin',
'title' => 'Can pin comments.',
'perm' => MSZ_CHANGELOG_PERM_PIN_COMMENTS,
'value' => manage_perms_value(
MSZ_CHANGELOG_PERM_PIN_COMMENTS,
$rawPerms['changelog_perms_allow'],
$rawPerms['changelog_perms_deny']
),
],
],
],
];
}

6
src/news.php Normal file
View file

@ -0,0 +1,6 @@
<?php
define('MSZ_NEWS_PERM_MANAGE_POSTS', 1);
define('MSZ_NEWS_PERM_MANAGE_CATEGORIES', 1 << 1);
define('MSZ_NEWS_PERM_DELETE_COMMENTS', 1 << 2);
define('MSZ_NEWS_PERM_EDIT_COMMENTS', 1 << 3);
define('MSZ_NEWS_PERM_PIN_COMMENTS', 1 << 4);

View file

@ -3,6 +3,18 @@ use Misuzu\Database;
define('MSZ_PERMS_USER', 'user');
define('MSZ_PERMS_CHANGELOG', 'changelog');
define('MSZ_PERMS_NEWS', 'news');
define('MSZ_PERM_MODES', [
MSZ_PERMS_USER, MSZ_PERMS_CHANGELOG, MSZ_PERMS_NEWS
]);
define('MSZ_PERMS_ALLOW', 'allow');
define('MSZ_PERMS_DENY', 'deny');
define('MSZ_PERM_SETS', [
MSZ_PERMS_ALLOW, MSZ_PERMS_DENY
]);
$_msz_perms_cache = [];
@ -29,9 +41,38 @@ function perms_is_cached(string $prefix, string $mode, int $pid): bool
return array_key_exists(perms_construct_cache_key($prefix, $mode, $pid), $_msz_perms_cache);
}
function perms_get_keys(): array
{
$perms = [];
foreach (MSZ_PERM_MODES as $mode) {
foreach (MSZ_PERM_SETS as $set) {
$perms[] = perms_get_key($mode, $set);
}
}
return $perms;
}
function perms_create(): array
{
$perms = [];
foreach (perms_get_keys() as $key) {
$perms[$key] = 0;
}
return $perms;
}
function perms_get_key(string $prefix, string $suffix): string
{
return $prefix . '_perms_' . $suffix;
}
function perms_get_user(string $prefix, int $user): int
{
if ($user < 1) {
if (!in_array($prefix, MSZ_PERM_MODES) || $user < 1) {
return 0;
} elseif ($user === 1) {
return 0x7FFFFFFF;
@ -71,7 +112,7 @@ function perms_get_user(string $prefix, int $user): int
function perms_get_role(string $prefix, int $role): int
{
if ($role < 1) {
if (!in_array($prefix, MSZ_PERM_MODES) || $role < 1) {
return 0;
}
@ -89,6 +130,66 @@ function perms_get_role(string $prefix, int $role): int
return perms_set_cache($prefix, 'role', $role, $getPerms->execute() ? (int)$getPerms->fetchColumn() : 0);
}
function perms_get_user_raw(int $user): array
{
$emptyPerms = perms_create();
if ($user < 1) {
return $emptyPerms;
}
$getPerms = Database::connection()->prepare('
SELECT
`' . implode('`, `', perms_get_keys()) . '`
FROM `msz_permissions`
WHERE `user_id` = :user_id
AND `role_id` IS NULL
');
$getPerms->bindValue('user_id', $user);
if (!$getPerms->execute()) {
return $emptyPerms;
}
$perms = $getPerms->fetch(PDO::FETCH_ASSOC);
if (!$perms) {
return $emptyPerms;
}
return $perms;
}
function perms_get_role_raw(int $role): array
{
$emptyPerms = perms_create();
if ($role < 1) {
return $emptyPerms;
}
$getPerms = Database::connection()->prepare("
SELECT
`' . implode('`, `', perms_get_keys()) . '`
FROM `msz_permissions`
WHERE `user_id` IS NULL
AND `role_id` = :role_id
");
$getPerms->bindValue('role_id', $role);
if (!$getPerms->execute()) {
return $emptyPerms;
}
$perms = $getPerms->fetch(PDO::FETCH_ASSOC);
if (!$perms) {
return $emptyPerms;
}
return $perms;
}
function perms_check(int $perms, int $perm): bool
{
return ($perms & $perm) > 0;

View file

@ -91,3 +91,53 @@
</ul>
{% endif %}
{% endmacro %}
{% macro permissions_table(permissions) %}
<div class="permissions">
{% for perms in permissions %}
<div class="permissions__line permissions__line--header">
<div class="permissions__title">
{{ perms.title }}
</div>
<div class="permissions__choice">
Yes
</div>
<div class="permissions__choice">
No
</div>
<div class="permissions__choice">
Never
</div>
</div>
{% for perm in perms.perms %}
<div class="permissions__line">
<div class="permissions__title">
{{ perm.title }}
</div>
<label class="permissions__choice permissions__choice--radio permissions__choice--yes">
<input {% if perm.value == 'yes' %}checked{% endif %}
class="permissions__input"
type="radio"
name="perms[{{ perms.section }}][{{ perm.section }}]"
value="yes">
</label>
<label class="permissions__choice permissions__choice--radio permissions__choice--no">
<input {% if perm.value == 'no' %}checked{% endif %}
class="permissions__input"
type="radio"
name="perms[{{ perms.section }}][{{ perm.section }}]"
value="no">
</label>
<label class="permissions__choice permissions__choice--radio permissions__choice--never">
<input {% if perm.value == 'never' %}checked{% endif %}
class="permissions__input"
type="radio"
name="perms[{{ perms.section }}][{{ perm.section }}]"
value="never">
</label>
</div>
{% endfor %}
{% endfor %}
</div>
{% endmacro %}

View file

@ -2,9 +2,11 @@
{% from '@manage/macros.twig' import pagination %}
{% block content %}
{% if can_manage_roles %}
<div class="container">
<a href="?v=role" class="button">Create new Role</a>
</div>
{% endif %}
<div class="container listing role-listing">
{% for role in manage_roles %}

View file

@ -1,4 +1,5 @@
{% extends '@manage/users/master.twig' %}
{% from '@manage/macros.twig' import permissions_table %}
{% block content %}
<form action="?v=role{{ edit_role is defined ? '&r=' ~ edit_role.role_id : '' }}" method="post">
@ -32,6 +33,13 @@
</div>
</label>
<label class="form__label">
<div class="form__label__text">Title</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ edit_role is defined ? edit_role.role_title : '' }}" name="role[title]" maxlength="64">
</div>
</label>
<h2 class="container__subtitle">Colour</h2>
<label class="form__label">
@ -70,10 +78,17 @@
<textarea class="input input--textarea" name="role[description]" maxlength="1000">{{ edit_role is defined ? edit_role.role_description : '' }}</textarea>
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">{{ edit_role is defined ? 'Update role' : 'Create role' }}</button>
</div>
{% if can_manage_perms %}
<div class="container">
<h2 class="container__subtitle">Permissions</h2>
{{ permissions_table(permissions) }}
</div>
{% endif %}
<div class="container">
<button class="button" name="csrf" value="{{ csrf_token() }}">{{ edit_role is defined ? 'Update role' : 'Create role' }}</button>
</div>
</form>
{% endblock %}

View file

@ -1,6 +1,9 @@
{% extends '@manage/users/master.twig' %}
{% from '@manage/macros.twig' import permissions_table %}
{% block content %}
{% if can_manage_users %}
<form method="post" enctype="multipart/form-data" action="">
<div class="container">
<h1 class="container__title">
Viewing <span style="{{ view_user.colour|html_colour }}">{{ view_user.username }}</span> ({{ view_user.user_id }})
@ -20,6 +23,27 @@
</div>
</label>
<label class="form__label">
<div class="form__label__text">Title</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_title }}" name="user[title]" maxlength="64">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Joined</div>
<div class="form__label__input">
<input class="input input--text" readonly type="text" value="{{ view_user.created_at }}">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Last online</div>
<div class="form__label__input">
<input class="input input--text" readonly type="text" value="{{ view_user.last_seen }}">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Register IP</div>
<div class="form__label__input">
@ -40,18 +64,10 @@
<input class="input input--text" readonly type="text" value="{{ view_user.user_country }}">
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">Update</button>
</div>
</div>
<form class="container" method="post" enctype="multipart/form-data" action="">
<h1 class="container__title">
Avatar
</h1>
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
<div class="container">
<h2 class="container__subtitle">Avatar</h2>
<label class="form__label">
<div class="form__label__text">New Avatar</div>
@ -60,142 +76,63 @@
</div>
</label>
<div>
<button class="button" name="avatar[mode]" value="upload">Upload</button>
<button class="button" name="avatar[mode]" value="delete">Delete</button>
<label class="form__label">
<div class="form__label__text">Delete Avatar</div>
<div class="form__label__input">
<input class="input" type="checkbox" name="avatar[delete]">
</div>
</label>
</div>
</form>
<div class="container">
<h1 class="container__title">
Password
</h1>
<h2 class="container__subtitle">Password</h2>
<label class="form__label">
<div class="form__label__text">New Password</div>
<div class="form__label__input">
<input class="input input--text" type="password" name="user[password]">
<input class="input input--text" type="password" name="password[new]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Confirm Password</div>
<div class="form__label__input">
<input class="input input--text" type="password" name="user[password_confirm]">
<input class="input input--text" type="password" name="password[confirm]">
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">Update</button>
</div>
</div>
<div class="container">
<h1 class="container__title">
Profile fields
</h1>
<h2 class="container__subtitle">Profile fields</h2>
{% for name, props in profile_fields %}
<label class="form__label">
<div class="form__label__text">Twitter</div>
<div class="form__label__text">{{ props.name }}</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_twitter }}" name="user[twitter]">
<input class="input input--text" type="{{ props.type|default('text') }}" value="{{ view_user['user_' ~ name] }}" name="profile[{{ name }}]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">osu!</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_osu }}" name="user[osu]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Website</div>
<div class="form__label__input">
<input class="input input--text" type="url" value="{{ view_user.user_website }}" name="user[website]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Youtube</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_youtube }}" name="user[youtube]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Steam</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_steam }}" name="user[steam]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Twitch.tv</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_twitchtv }}" name="user[twitchtv]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Last.fm</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_twitchtv }}" name="user[twitchtv]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Github</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_github }}" name="user[github]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Skype</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_skype }}" name="user[skype]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Discord</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_discord }}" name="user[discord]">
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">Update</button>
</div>
</div>
{% if available_roles|length > 0 %}
<form class="container" method="post" action="">
<h1 class="container__title">Add Role</h1>
<label class="form__label">
<div class="form__label__text">Available Roles</div>
<div class="form__label__input">
<select name="add_role[role]" class="input input--select">
{% for role in available_roles %}
<option value="{{ role.role_id }}">
{{ role.role_name }}
</option>
{% endfor %}
</select>
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">Add</button>
{% if can_manage_perms %}
<div class="container">
<h2 class="container__subtitle">Permissions</h2>
{{ permissions_table(permissions) }}
</div>
{% endif %}
<div class="container">
<button class="button" name="csrf" value="{{ csrf_token() }}">Update</button>
</div>
</form>
{% endif %}
{% if can_manage_users %}
<div class="container">
{% if has_roles|length > 0 %}
<form class="container" method="post" action="">
<h1 class="container__title">Manage Roles</h1>
<form method="post" action="" style="display:inline-block">
<h2 class="container__subtitle">Manage Roles</h2>
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
@ -219,11 +156,28 @@
</form>
{% endif %}
<div class="container">
<h1 class="container__title">Permissions</h1>
</div>
{% if available_roles|length > 0 %}
<form method="post" action="" style="display:inline-block">
<h2 class="container__subtitle">Add Role</h2>
<div class="container">
<h1 class="container__title">Sessions</h1>
<label class="form__label">
<div class="form__label__text">Available Roles</div>
<div class="form__label__input">
<select name="add_role[role]" class="input input--select">
{% for role in available_roles %}
<option value="{{ role.role_id }}">
{{ role.role_name }}
</option>
{% endfor %}
</select>
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">Add</button>
</div>
</form>
{% endif %}
</div>
{% endif %}
{% endblock %}