Admin: Add permissions management and role assignment - refs #5644

Author: @christianbeeznest
pull/5654/head
christianbeeznest 1 year ago committed by GitHub
parent 18e8c0a65b
commit 4526fcf836
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 48
      assets/css/app.scss
  2. 15
      public/main/inc/lib/api.lib.php
  3. 103
      src/CoreBundle/Controller/PermissionController.php
  4. 262
      src/CoreBundle/DataFixtures/PermissionFixtures.php
  5. 81
      src/CoreBundle/Entity/Permission.php
  6. 89
      src/CoreBundle/Entity/PermissionRelRole.php
  7. 33
      src/CoreBundle/Form/PermissionType.php
  8. 51
      src/CoreBundle/Migrations/Schema/V200/Version20240709222600.php
  9. 74
      src/CoreBundle/Migrations/Schema/V200/Version20240709222700.php
  10. 23
      src/CoreBundle/Repository/PermissionRelRoleRepository.php
  11. 23
      src/CoreBundle/Repository/PermissionRepository.php
  12. 4
      src/CoreBundle/Resources/config/services.yml
  13. 49
      src/CoreBundle/Resources/views/Permission/index.html.twig
  14. 35
      src/CoreBundle/Service/PermissionService.php

@ -708,6 +708,54 @@ form .field {
background-color: #dc3545;
}
.permissions-table {
width: 100%;
border-collapse: collapse;
th, td {
border: 1px solid #ccc;
padding: 8px;
text-align: center;
}
th {
background-color: #f9f9f9;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
tr:hover {
background-color: #e9e9e9;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
}
th {
padding-top: 12px;
padding-bottom: 12px;
text-align: center;
background-color: #999;
color: white;
}
td {
padding: 12px;
}
input[type="checkbox"] {
transform: scale(1.2);
}
.save-button {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
margin: 20px 0;
border: none;
cursor: pointer;
text-align: center;
}
.save-button:hover {
background-color: #45a049;
}
}
//@import 'primevue-md-light-indigo/theme.css';
//@import '~primevue/resources/primevue.min.css';
//@import '~primeflex/primeflex.css';

@ -11,6 +11,7 @@ use Chamilo\CoreBundle\Entity\User;
use Chamilo\CoreBundle\Entity\UserCourseCategory;
use Chamilo\CoreBundle\Exception\NotAllowedException;
use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CoreBundle\Service\PermissionService;
use Chamilo\CoreBundle\ServiceHelper\MailHelper;
use Chamilo\CoreBundle\ServiceHelper\ThemeHelper;
use Chamilo\CourseBundle\Entity\CGroup;
@ -7473,3 +7474,17 @@ function api_protect_webservices()
exit;
}
}
/**
* Checks if a set of roles have a specific permission.
*
* @param string $permissionSlug The slug of the permission to check.
* @param array $roles An array of role codes to check against.
* @return bool True if any of the roles have the permission, false otherwise.
*/
function api_get_permission(string $permissionSlug, array $roles): bool
{
$permissionService = Container::$container->get(PermissionService::class);
return $permissionService->hasPermission($permissionSlug, $roles);
}

@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Controller;
use Chamilo\CoreBundle\Entity\PermissionRelRole;
use Chamilo\CoreBundle\Form\PermissionType;
use Chamilo\CoreBundle\Repository\PermissionRelRoleRepository;
use Chamilo\CoreBundle\Repository\PermissionRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* The Permission controller manages the /permissions page to control what roles has what permission
*/
class PermissionController extends AbstractController
{
#[IsGranted('ROLE_ADMIN')]
#[Route('/permissions/test', name: 'permissions_test')]
public function testPermissions(): Response
{
// Test roles and permission slug
$roles = ['ROLE_STUDENT', 'ROLE_TEACHER'];
$permissionSlug = 'analytics:view';
// Call the api_get_permission function and log the result
$hasPermission = api_get_permission($permissionSlug, $roles);
error_log('Permission check for ' . $permissionSlug . ' with roles ' . implode(', ', $roles) . ': ' . ($hasPermission ? 'true' : 'false'));
// Return a simple response for testing purposes
return new Response('<html><body>Permission check result: ' . ($hasPermission ? 'true' : 'false') . '</body></html>');
}
#[IsGranted('ROLE_ADMIN')]
#[Route('/permissions', name: 'permissions')]
public function index(
PermissionRepository $permissionRepo,
PermissionRelRoleRepository $permissionRelRoleRepo,
Request $request,
EntityManagerInterface $em
): Response {
$permissions = $permissionRepo->findAll();
$roles = ['ROLE_INVITEE', 'ROLE_STUDENT', 'ROLE_TEACHER', 'ROLE_ADMIN', 'ROLE_SUPER_ADMIN', 'ROLE_GLOBAL_ADMIN', 'ROLE_RRHH', 'ROLE_QUESTION_MANAGER', 'ROLE_SESSION_MANAGER', 'ROLE_STUDENT_BOSS'];
if ($request->isMethod('POST')) {
$data = $request->request->all('permissions');
foreach ($permissions as $permission) {
foreach ($roles as $role) {
$checkboxValue = isset($data[$permission->getSlug()][$role]);
error_log('Processing role: ' . $role . ' with value: ' . ($checkboxValue ? 'true' : 'false'));
$permRelRole = $permissionRelRoleRepo->findOneBy(['permission' => $permission, 'roleCode' => $role]);
if ($checkboxValue) {
if (!$permRelRole) {
$permRelRole = new PermissionRelRole();
$permRelRole->setPermission($permission);
$permRelRole->setRoleCode($role);
}
$permRelRole->setChangeable(true);
$permRelRole->setUpdatedAt(new \DateTime());
$em->persist($permRelRole);
error_log('Persisting PermissionRelRole for permission: ' . $permission->getSlug() . ' and role: ' . $role);
} else {
if ($permRelRole) {
$em->remove($permRelRole);
error_log('Removing PermissionRelRole for permission: ' . $permission->getSlug() . ' and role: ' . $role);
}
}
}
}
$em->flush();
error_log('Flush complete');
return $this->redirectToRoute('permissions');
}
$forms = [];
foreach ($permissions as $permission) {
$defaultData = [];
foreach ($roles as $role) {
$permRelRole = $permissionRelRoleRepo->findOneBy(['permission' => $permission, 'roleCode' => $role]);
$defaultData[$role] = $permRelRole ? $permRelRole->isChangeable() : false;
}
$form = $this->createForm(PermissionType::class, $defaultData, ['roles' => $roles]);
$forms[$permission->getSlug()] = $form->createView();
}
return $this->render('@ChamiloCore/Permission/index.html.twig', [
'permissions' => $permissions,
'forms' => $forms,
'roles' => $roles
]);
}
}

@ -0,0 +1,262 @@
<?php
/* For licensing terms, see /license.txt */
declare(strict_types=1);
namespace Chamilo\CoreBundle\DataFixtures;
use Chamilo\CoreBundle\Entity\Permission;
use Chamilo\CoreBundle\Entity\PermissionRelRole;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
use Doctrine\Persistence\ObjectManager;
class PermissionFixtures extends Fixture implements FixtureGroupInterface
{
public static function getGroups(): array
{
return ['permissions'];
}
public function load(ObjectManager $manager): void
{
$permissions = self::getPermissions();
$roles = self::getRoles();
$permissionsMapping = self::getPermissionsMapping();
foreach ($permissions as $permData) {
$existingPermission = $manager->getRepository(Permission::class)->findOneBy(['slug' => $permData['slug']]);
if ($existingPermission) {
continue;
}
$permission = new Permission();
$permission->setTitle($permData['title']);
$permission->setSlug($permData['slug']);
$permission->setDescription($permData['description']);
$manager->persist($permission);
$manager->flush();
foreach ($roles as $roleName => $roleCode) {
if (in_array($roleCode, $permissionsMapping[$permData['slug']])) {
$permRelRole = new PermissionRelRole();
$permRelRole->setPermission($permission);
$permRelRole->setRoleCode($roleName);
$permRelRole->setChangeable(true);
$permRelRole->setUpdatedAt(new \DateTime());
$manager->persist($permRelRole);
}
}
}
$manager->flush();
}
public static function getPermissions(): array
{
return [
['title' => 'View Analytics', 'slug' => 'analytics:view', 'description' => 'View analytics data'],
['title' => 'View Assigned Analytics', 'slug' => 'analytics:viewassigned', 'description' => 'View results of users assigned to me'],
['title' => 'View All Analytics', 'slug' => 'analytics:viewall', 'description' => 'View results of all users'],
['title' => 'Create Assignment', 'slug' => 'assignment:create', 'description' => 'Create assignments'],
['title' => 'Delete Assignment', 'slug' => 'assignment:delete', 'description' => 'Delete assignments'],
['title' => 'Edit Assignment', 'slug' => 'assignment:edit', 'description' => 'Edit assignments'],
['title' => 'Grade Assignment', 'slug' => 'assignment:grade', 'description' => 'Grade assignments'],
['title' => 'Submit Assignment', 'slug' => 'assignment:submit', 'description' => 'Submit assignments'],
['title' => 'View Assignment', 'slug' => 'assignment:view', 'description' => 'View assignments'],
['title' => 'Backup', 'slug' => 'backup:backup', 'description' => 'Backup'],
['title' => 'Copy Backup', 'slug' => 'backup:copy', 'description' => 'Copy backup'],
['title' => 'Restore Backup', 'slug' => 'backup:restore', 'description' => 'Restore backup'],
['title' => 'Configure Badge Criteria', 'slug' => 'badge:configurecriteria', 'description' => 'Configure badge criteria'],
['title' => 'Create Badge', 'slug' => 'badge:create', 'description' => 'Create badges'],
['title' => 'Edit Badge', 'slug' => 'badge:edit', 'description' => 'Edit badges'],
['title' => 'Delete Badge', 'slug' => 'badge:delete', 'description' => 'Delete badges'],
['title' => 'View Badge', 'slug' => 'badge:view', 'description' => 'View badges'],
['title' => 'Create Calendar Event', 'slug' => 'calendar:create', 'description' => 'Create calendar events'],
['title' => 'Edit Calendar Event', 'slug' => 'calendar:edit', 'description' => 'Edit calendar events'],
['title' => 'Delete Calendar Event', 'slug' => 'calendar:delete', 'description' => 'Delete calendar events'],
['title' => 'View Courses Catalogue', 'slug' => 'catalogue:view', 'description' => 'View courses catalogue'],
['title' => 'Create Certificate Template', 'slug' => 'certificate:create', 'description' => 'Create certificate templates'],
['title' => 'Delete Certificate Template', 'slug' => 'certificate:delete', 'description' => 'Delete certificate templates'],
['title' => 'Edit Certificate Template', 'slug' => 'certificate:edit', 'description' => 'Edit certificate templates'],
['title' => 'Generate Certificate', 'slug' => 'certificate:generate', 'description' => 'Generate certificates'],
['title' => 'Generate All Certificates', 'slug' => 'certificate:generateall', 'description' => 'Generate all certificates in a gradebook'],
['title' => 'View All Certificates', 'slug' => 'certificate:viewall', 'description' => 'View all instances of one certificate issued to all users'],
['title' => 'Assign Course to Class', 'slug' => 'class:assigncourse', 'description' => 'Assign a course to a class'],
['title' => 'Assign Session to Class', 'slug' => 'class:assignsession', 'description' => 'Assign a session to a class'],
['title' => 'Assign User to Class', 'slug' => 'class:assignuser', 'description' => 'Assign a user to a class'],
['title' => 'Create Class', 'slug' => 'class:create', 'description' => 'Manage global classes of users'],
['title' => 'Delete Class', 'slug' => 'class:delete', 'description' => 'Delete classes'],
['title' => 'Edit Class', 'slug' => 'class:edit', 'description' => 'Edit classes'],
['title' => 'View Class', 'slug' => 'class:view', 'description' => 'View classes'],
['title' => 'Create CMS Page', 'slug' => 'cms:create', 'description' => 'Create CMS pages'],
['title' => 'Delete CMS Page', 'slug' => 'cms:delete', 'description' => 'Delete CMS pages'],
['title' => 'Edit CMS Page', 'slug' => 'cms:edit', 'description' => 'Edit CMS pages'],
['title' => 'Create Course Space', 'slug' => 'course:create', 'description' => 'Create course spaces'],
['title' => 'Delete Course Space', 'slug' => 'course:delete', 'description' => 'Delete course spaces'],
['title' => 'Download Course Content', 'slug' => 'course:downloadcoursecontent', 'description' => 'Download all course content'],
['title' => 'Edit Own Course Properties', 'slug' => 'course:edit', 'description' => 'Edit own course\'s properties'],
['title' => 'Edit All Course Properties', 'slug' => 'course:editall', 'description' => 'Edit all course\'s properties'],
['title' => 'Manage Plugins', 'slug' => 'plugin:manage', 'description' => 'Enable/disable/configure plugins'],
['title' => 'Create Quiz', 'slug' => 'quiz:create', 'description' => 'Create quizzes'],
['title' => 'Delete Quiz', 'slug' => 'quiz:delete', 'description' => 'Delete quizzes'],
['title' => 'Edit Quiz', 'slug' => 'quiz:edit', 'description' => 'Edit quizzes'],
['title' => 'Grade Quiz', 'slug' => 'quiz:grade', 'description' => 'Grade quizzes'],
['title' => 'View Live Quiz Results', 'slug' => 'quiz:viewliveresults', 'description' => 'View live quiz results'],
['title' => 'Manage Question Bank', 'slug' => 'quiz:managequestionbank', 'description' => 'Manage question bank'],
['title' => 'Create Role', 'slug' => 'role:create', 'description' => 'Create roles'],
['title' => 'Manage Role Permissions', 'slug' => 'role:managepermissions', 'description' => 'Assign or remove permissions from roles'],
['title' => 'Create Session', 'slug' => 'session:create', 'description' => 'Create sessions'],
['title' => 'Delete Session', 'slug' => 'session:delete', 'description' => 'Delete sessions'],
['title' => 'Edit Own Session Properties', 'slug' => 'session:edit', 'description' => 'Edit own session\'s properties'],
['title' => 'Edit All Session Properties', 'slug' => 'session:editall', 'description' => 'Edit all session\'s properties'],
['title' => 'Assign Course to Session', 'slug' => 'session:assigncourse', 'description' => 'Assign a course to a session'],
['title' => 'Edit Site Settings', 'slug' => 'site:editsettings', 'description' => 'Manage settings of the platform'],
['title' => 'Access Site Maintenance', 'slug' => 'site:maintenanceaccess', 'description' => 'Access site maintenance'],
['title' => 'Manage Course Competency', 'slug' => 'skill:coursecompetencymanage', 'description' => 'Assign skills through course gradebooks'],
['title' => 'Review User Competency', 'slug' => 'skill:usercompetencyreview', 'description' => 'Add comments on other user\'s acquired skills'],
['title' => 'Assign Skill', 'slug' => 'skill:assign', 'description' => 'Assign a skill to a user'],
['title' => 'Create Skill', 'slug' => 'skill:create', 'description' => 'Create skills'],
['title' => 'Delete Skill', 'slug' => 'skill:delete', 'description' => 'Delete skills'],
['title' => 'Edit Skill', 'slug' => 'skill:edit', 'description' => 'Edit skills'],
['title' => 'View Skill', 'slug' => 'skill:view', 'description' => 'View all skills acquired by users in my context'],
['title' => 'View All Skills', 'slug' => 'skill:viewall', 'description' => 'View all skills acquired by users of the platform'],
['title' => 'Create Survey', 'slug' => 'survey:create', 'description' => 'Add a survey (global or inside own course)'],
['title' => 'Delete Survey', 'slug' => 'survey:delete', 'description' => 'Delete surveys'],
['title' => 'Edit Survey', 'slug' => 'survey:edit', 'description' => 'Edit surveys'],
['title' => 'Submit Survey', 'slug' => 'survey:submit', 'description' => 'Submit surveys'],
['title' => 'View Survey Results', 'slug' => 'survey:viewresults', 'description' => 'View survey results'],
['title' => 'Comment on Ticket', 'slug' => 'ticket:comment', 'description' => 'Comment on tickets'],
['title' => 'Manage Tickets', 'slug' => 'ticket:manage', 'description' => 'Manage the tickets system'],
['title' => 'Report Ticket', 'slug' => 'ticket:report', 'description' => 'Report tickets'],
['title' => 'See Ticket Issues', 'slug' => 'ticket:seeissues', 'description' => 'See issue details for issues where they are involved'],
['title' => 'View All Ticket Issues', 'slug' => 'ticket:viewallissues', 'description' => 'View all issues'],
['title' => 'Edit Tool Visibility', 'slug' => 'tool:editvisibility', 'description' => 'Allow setting the visibility of a tool in a course'],
['title' => 'Manage URL', 'slug' => 'url:manage', 'description' => 'Manage Multi-URL configuration'],
['title' => 'Assign Classes to URL', 'slug' => 'url:assignclass', 'description' => 'Assign classes to URL'],
['title' => 'Assign Courses to URL', 'slug' => 'url:assigncourse', 'description' => 'Assign courses to URL'],
['title' => 'Assign Users to URL', 'slug' => 'url:assignuser', 'description' => 'Assign users to URL'],
['title' => 'Assign User to Class', 'slug' => 'user:assignclass', 'description' => 'Assign a user to a class'],
['title' => 'Assign User to Course', 'slug' => 'user:assigncourse', 'description' => 'Assign a user to a course'],
['title' => 'Assign User to Session', 'slug' => 'user:assignsession', 'description' => 'Assign a user to a session'],
['title' => 'Create User', 'slug' => 'user:create', 'description' => 'Create users'],
['title' => 'Delete User', 'slug' => 'user:delete', 'description' => 'Delete users'],
['title' => 'Edit User', 'slug' => 'user:edit', 'description' => 'Edit users'],
['title' => 'Edit User Role', 'slug' => 'user:editrole', 'description' => 'Edit user roles'],
['title' => 'Login As User', 'slug' => 'user:loginas', 'description' => 'Login as another user'],
];
}
public static function getRoles(): array
{
return [
'ROLE_INVITEE' => 'INV',
'ROLE_STUDENT' => 'STU',
'ROLE_TEACHER' => 'TEA',
'ROLE_ADMIN' => 'ADM',
'ROLE_SUPER_ADMIN' => 'SUA',
'ROLE_GLOBAL_ADMIN' => 'GLO',
'ROLE_RRHH' => 'HRM',
'ROLE_QUESTION_MANAGER' => 'QBM',
'ROLE_SESSION_MANAGER' => 'SSM',
'ROLE_STUDENT_BOSS' => 'STB',
];
}
public static function getPermissionsMapping(): array
{
return [
'analytics:view' => ['INV', 'STU', 'TEA', 'ADM', 'SUA', 'GLO', 'HRM', 'QBM', 'SSM', 'STB'],
'analytics:viewassigned' => ['TEA', 'ADM', 'SUA', 'GLO', 'HRM', 'SSM', 'STB'],
'analytics:viewall' => ['ADM', 'SUA', 'GLO', 'SSM', 'STB'],
'assignment:create' => ['TEA'],
'assignment:delete' => ['TEA', 'ADM', 'SUA', 'GLO'],
'assignment:edit' => ['TEA', 'ADM', 'SUA', 'GLO'],
'assignment:grade' => ['TEA'],
'assignment:submit' => ['STU'],
'assignment:view' => ['INV', 'STU', 'TEA', 'ADM', 'SUA', 'GLO', 'HRM', 'SSM', 'STB'],
'backup:backup' => ['TEA', 'ADM', 'SUA', 'GLO', 'SSM'],
'backup:copy' => ['TEA', 'ADM', 'SUA', 'GLO', 'SSM'],
'backup:restore' => ['TEA', 'ADM', 'SUA', 'GLO', 'SSM'],
'badge:configurecriteria' => ['ADM', 'SUA', 'GLO', 'SSM'],
'badge:create' => ['ADM', 'SUA', 'GLO', 'SSM'],
'badge:edit' => ['ADM', 'SUA', 'GLO', 'SSM'],
'badge:delete' => ['ADM', 'SUA', 'GLO', 'SSM'],
'badge:view' => ['INV', 'STU', 'TEA', 'ADM', 'SUA', 'GLO', 'SSM', 'STB'],
'calendar:create' => ['ADM', 'SUA', 'GLO'],
'calendar:edit' => ['ADM', 'SUA', 'GLO'],
'calendar:delete' => ['ADM', 'SUA', 'GLO'],
'catalogue:view' => ['INV', 'STU', 'TEA', 'ADM', 'SUA', 'GLO', 'HRM', 'QBM', 'SSM', 'STB'],
'certificate:create' => ['TEA', 'SSM'],
'certificate:delete' => ['TEA', 'SSM'],
'certificate:edit' => ['TEA', 'SSM'],
'certificate:generate' => ['STU', 'TEA', 'SSM'],
'certificate:generateall' => ['TEA', 'HRM', 'SSM'],
'certificate:viewall' => ['TEA', 'HRM', 'SSM', 'STB'],
'class:assigncourse' => ['TEA', 'ADM', 'SUA', 'GLO'],
'class:assignsession' => ['ADM', 'SUA', 'GLO', 'SSM'],
'class:assignuser' => ['ADM', 'SUA', 'GLO', 'SSM'],
'class:create' => ['ADM', 'SUA', 'GLO', 'SSM'],
'class:delete' => ['ADM', 'SUA', 'GLO', 'SSM'],
'class:edit' => ['ADM', 'SUA', 'GLO', 'SSM'],
'class:view' => ['STU', 'TEA', 'ADM', 'SUA', 'GLO', 'SSM'],
'cms:create' => ['ADM', 'SUA', 'GLO'],
'cms:delete' => ['ADM', 'SUA', 'GLO'],
'cms:edit' => ['ADM', 'SUA', 'GLO'],
'course:create' => ['TEA', 'ADM', 'SUA', 'GLO', 'SSM'],
'course:delete' => ['TEA', 'ADM', 'SUA', 'GLO'],
'course:downloadcoursecontent' => ['TEA', 'ADM', 'SUA', 'GLO', 'SSM'],
'course:edit' => ['TEA', 'SSM'],
'course:editall' => ['ADM', 'SUA', 'GLO'],
'plugin:manage' => ['ADM', 'SUA', 'GLO'],
'quiz:create' => ['TEA', 'QBM'],
'quiz:delete' => ['TEA', 'QBM'],
'quiz:edit' => ['TEA', 'QBM'],
'quiz:grade' => ['TEA'],
'quiz:viewliveresults' => ['TEA', 'SSM'],
'quiz:managequestionbank' => ['ADM', 'SUA', 'GLO', 'QBM'],
'role:create' => ['ADM', 'SUA', 'GLO'],
'role:managepermissions' => ['ADM', 'SUA', 'GLO'],
'session:create' => ['ADM', 'SUA', 'GLO', 'SSM'],
'session:delete' => ['ADM', 'SUA', 'GLO', 'SSM'],
'session:edit' => ['ADM', 'SUA', 'GLO', 'SSM'],
'session:editall' => ['ADM', 'SUA', 'GLO', 'SSM'],
'session:assigncourse' => ['ADM', 'SUA', 'GLO', 'SSM'],
'site:editsettings' => ['ADM', 'SUA', 'GLO'],
'site:maintenanceaccess' => ['ADM', 'SUA', 'GLO'],
'skill:coursecompetencymanage' => ['TEA', 'ADM', 'SUA', 'GLO', 'HRM'],
'skill:usercompetencyreview' => ['STU', 'TEA', 'ADM', 'SUA', 'GLO'],
'skill:assign' => ['ADM', 'SUA', 'GLO'],
'skill:create' => ['GLO'],
'skill:delete' => ['GLO'],
'skill:edit' => ['GLO'],
'skill:view' => ['ADM', 'SUA', 'GLO', 'SSM', 'STB'],
'skill:viewall' => ['ADM', 'SUA', 'GLO', 'SSM', 'STB'],
'survey:create' => ['TEA'],
'survey:delete' => ['TEA'],
'survey:edit' => ['TEA'],
'survey:submit' => ['INV', 'STU', 'TEA', 'ADM', 'SUA', 'GLO', 'SSM', 'STB'],
'survey:viewresults' => ['TEA', 'HRM', 'SSM', 'STB'],
'ticket:comment' => ['STU', 'TEA', 'ADM', 'SUA', 'GLO', 'HRM', 'QBM', 'SSM', 'STB'],
'ticket:manage' => ['ADM', 'SUA', 'GLO'],
'ticket:report' => ['STU', 'TEA', 'ADM', 'SUA', 'GLO', 'HRM', 'QBM', 'SSM', 'STB'],
'ticket:seeissues' => ['STU', 'TEA', 'ADM', 'SUA', 'GLO', 'SSM', 'STB'],
'ticket:viewallissues' => ['ADM', 'SUA', 'GLO', 'SSM', 'STB'],
'tool:editvisibility' => ['TEA', 'ADM', 'SUA', 'GLO', 'SSM'],
'url:manage' => ['GLO'],
'url:assignclass' => ['GLO'],
'url:assigncourse' => ['GLO'],
'url:assignuser' => ['GLO'],
'user:assignclass' => ['ADM', 'SUA', 'GLO', 'SSM'],
'user:assigncourse' => ['TEA', 'ADM', 'SUA', 'GLO'],
'user:assignsession' => ['ADM', 'SUA', 'GLO', 'SSM'],
'user:create' => ['ADM', 'SUA', 'GLO'],
'user:delete' => ['ADM', 'SUA', 'GLO'],
'user:edit' => ['ADM', 'SUA', 'GLO'],
'user:editrole' => ['ADM', 'SUA', 'GLO'],
'user:loginas' => ['SUA', 'GLO'],
];
}
}

@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Entity;
use Chamilo\CoreBundle\Repository\PermissionRepository;
use Doctrine\ORM\Mapping as ORM;
use Stringable;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: PermissionRepository::class)]
#[ORM\Table(name: 'permission')]
/**
* A Permission defines something a user role can do.
* The permissions a role has is determined by the PermissionRelRole entity.
*/
class Permission implements Stringable
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[Assert\NotBlank]
#[ORM\Column(type: 'string', length: 255)]
private string $title;
#[Assert\NotBlank]
#[ORM\Column(type: 'string', length: 255, unique: true)]
private string $slug;
#[ORM\Column(type: 'text', nullable: true)]
private ?string $description = null;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getSlug(): string
{
return $this->slug;
}
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function __toString(): string
{
return $this->title;
}
}

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Entity;
use Chamilo\CoreBundle\Repository\PermissionRelRoleRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: PermissionRelRoleRepository::class)]
#[ORM\Table(name: 'permission_rel_role')]
/**
* The PermissionRelRole entity makes the link between roles
* (defined in security.yaml) and permissions (defined by the
* Permission entity) to define which user role can do what.
*/
class PermissionRelRole
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\ManyToOne(targetEntity: Permission::class)]
#[ORM\JoinColumn(nullable: false)]
private Permission $permission;
#[Assert\NotBlank]
#[ORM\Column(type: 'string', length: 50)]
private string $roleCode;
#[ORM\Column(type: 'boolean')]
private bool $changeable;
#[ORM\Column(type: 'datetime')]
private \DateTime $updatedAt;
public function getId(): ?int
{
return $this->id;
}
public function getPermission(): Permission
{
return $this->permission;
}
public function setPermission(Permission $permission): self
{
$this->permission = $permission;
return $this;
}
public function getRoleCode(): string
{
return $this->roleCode;
}
public function setRoleCode(string $roleCode): self
{
$this->roleCode = $roleCode;
return $this;
}
public function isChangeable(): bool
{
return $this->changeable;
}
public function setChangeable(bool $changeable): self
{
$this->changeable = $changeable;
return $this;
}
public function getUpdatedAt(): \DateTime
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTime $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
}

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PermissionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$roles = $options['roles'];
foreach ($roles as $role) {
$builder->add($role, CheckboxType::class, [
'required' => false,
]);
}
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'roles' => [],
]);
}
}

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema;
final class Version20240709222600 extends AbstractMigrationChamilo
{
public function getDescription(): string
{
return 'Create permission and permission_rel_role tables';
}
public function up(Schema $schema): void
{
$this->addSql('
CREATE TABLE IF NOT EXISTS permission (
id INT AUTO_INCREMENT NOT NULL,
title VARCHAR(255) NOT NULL,
slug VARCHAR(255) NOT NULL,
description LONGTEXT DEFAULT NULL,
UNIQUE INDEX UNIQ_2DEDCC6F989D9B62 (slug),
PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB ROW_FORMAT = DYNAMIC
');
$this->addSql('
CREATE TABLE IF NOT EXISTS permission_rel_role (
id INT AUTO_INCREMENT NOT NULL,
permission_id INT NOT NULL,
role_code VARCHAR(50) NOT NULL,
changeable TINYINT(1) NOT NULL,
updated_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime)\',
INDEX IDX_43723A27FED90CCA (permission_id),
PRIMARY KEY(id),
CONSTRAINT FK_43723A27FED90CCA FOREIGN KEY (permission_id) REFERENCES permission (id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB ROW_FORMAT = DYNAMIC
');
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE IF EXISTS permission_rel_role');
$this->addSql('DROP TABLE IF EXISTS permission');
}
}

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Migrations\Schema\V200;
use Chamilo\CoreBundle\DataFixtures\PermissionFixtures;
use Chamilo\CoreBundle\Entity\Permission;
use Chamilo\CoreBundle\Entity\PermissionRelRole;
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
use Doctrine\DBAL\Schema\Schema;
final class Version20240709222700 extends AbstractMigrationChamilo
{
public function getDescription(): string
{
return 'Insert default data into permission and permission_rel_role tables';
}
public function up(Schema $schema): void
{
$permissions = PermissionFixtures::getPermissions();
$roles = PermissionFixtures::getRoles();
$permissionsMapping = PermissionFixtures::getPermissionsMapping();
foreach ($permissions as $permData) {
$permissionRepository = $this->entityManager->getRepository(Permission::class);
$existingPermission = $permissionRepository->findOneBy(['slug' => $permData['slug']]);
if ($existingPermission) {
$permission = $existingPermission;
} else {
$permission = new Permission();
$permission->setTitle($permData['title']);
$permission->setSlug($permData['slug']);
$permission->setDescription($permData['description']);
$this->entityManager->persist($permission);
$this->entityManager->flush();
}
foreach ($roles as $roleName => $roleCode) {
if (in_array($roleCode, $permissionsMapping[$permData['slug']])) {
$permissionRelRoleRepository = $this->entityManager->getRepository(PermissionRelRole::class);
$existingRelation = $permissionRelRoleRepository->findOneBy([
'permission' => $permission,
'roleCode' => $roleName
]);
if ($existingRelation) {
continue;
}
$permissionRelRole = new PermissionRelRole();
$permissionRelRole->setPermission($permission);
$permissionRelRole->setRoleCode($roleName);
$permissionRelRole->setChangeable(true);
$permissionRelRole->setUpdatedAt(new \DateTime());
$this->entityManager->persist($permissionRelRole);
$this->entityManager->flush();
}
}
}
}
public function down(Schema $schema): void
{
$this->addSql('DELETE FROM permission_rel_role');
$this->addSql('DELETE FROM permission');
}
}

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Repository;
use Chamilo\CoreBundle\Entity\PermissionRelRole;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<PermissionRelRole>
*/
class PermissionRelRoleRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, PermissionRelRole::class);
}
}

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Repository;
use Chamilo\CoreBundle\Entity\Permission;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Permission>
*/
class PermissionRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Permission::class);
}
}

@ -103,3 +103,7 @@ services:
Chamilo\CoreBundle\Filter\SocialWallFilter:
tags: [ 'api_platform.filter' ]
Chamilo\CoreBundle\Service\PermissionService:
arguments:
$permissionRelRoleRepository: '@Chamilo\CoreBundle\Repository\PermissionRelRoleRepository'

@ -0,0 +1,49 @@
{% extends "@ChamiloCore/Layout/layout_one_col.html.twig" %}
{% block content %}
<h1>{{ 'Permissions Management'|trans }}</h1>
<form method="post">
<button type="submit" class="save-button btn btn--primary hover:bg-blue-700 text-white font-bold py-2 px-4 rounded cursor-pointer mt-4 mb-4">{{ 'Save permissions'|trans }}</button>
<table class="permissions-table">
<thead>
<tr>
<th>{{ 'Permission '|trans }} <br> ({{ 'slug'|trans }})</th>
{% for role in roles %}
<th>
{{ role|trans }}<br>
<input type="checkbox" class="select-all" data-role="{{ role }}" style="margin-top: 5px;">
</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for permission in permissions %}
<tr>
<td>{{ permission.title|trans }} <br> ({{ permission.slug }})</td>
{% for role in roles %}
<td>
<input type="checkbox" name="permissions[{{ permission.slug }}][{{ role }}]"
{% if forms[permission.slug].vars.value[role] %}checked="checked"{% endif %}>
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit" class="save-button btn btn--primary hover:bg-blue-700 text-white font-bold py-2 px-4 rounded cursor-pointer mt-4">{{ 'Save permissions'|trans }}</button>
</form>
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll('.select-all').forEach((checkbox) => {
checkbox.addEventListener('click', function() {
const role = this.getAttribute('data-role');
const checkboxes = document.querySelectorAll(`input[name*="[${role}]"]`);
const isChecked = this.checked;
checkboxes.forEach((cb) => {
cb.checked = isChecked;
});
});
});
});
</script>
{% endblock %}

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Service;
use Chamilo\CoreBundle\Repository\PermissionRelRoleRepository;
class PermissionService
{
private PermissionRelRoleRepository $permissionRelRoleRepository;
public function __construct(PermissionRelRoleRepository $permissionRelRoleRepository)
{
$this->permissionRelRoleRepository = $permissionRelRoleRepository;
}
public function hasPermission(string $permissionSlug, array $roles): bool
{
$queryBuilder = $this->permissionRelRoleRepository->createQueryBuilder('prr')
->innerJoin('prr.permission', 'p')
->where('p.slug = :permissionSlug')
->andWhere('prr.roleCode IN (:roles)')
->andWhere('prr.changeable = :changeable')
->setParameter('permissionSlug', $permissionSlug)
->setParameter('roles', $roles)
->setParameter('changeable', true);
$results = $queryBuilder->getQuery()->getResult();
return count($results) > 0;
}
}
Loading…
Cancel
Save