<?php
namespace Rawafed\SecurityBundle\Loader;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Dumper;
use Symfony\Component\Yaml\Exception\ParseException;
class UserRolesHierarchyLoader extends RoleHierarchy
{
/**
* @var ContainerInterface
*/
private $container;
private $isDebugMode;
private $cacheFilePath;
/**
* @param array $hierarchy
*/
public function __construct(array $hierarchy, ContainerInterface $container, $cacheDir, $isDebugMode = true )
{
$this->container = $container;
$this->isDebugMode = (bool) $isDebugMode;
$this->cacheFilePath = $cacheDir; // . DIRECTORY_SEPARATOR . "roles_hierarchy.cache.yml";
// merging recursivly the default array of roles hierarchy in #app/config/security.yml with the roles hierarchy exists in roles database table
$hierarchy = array_merge_recursive ($hierarchy, $this->buildRolesTree());
parent::__construct( $hierarchy );
}
/**
* Here we build an array with roles. It looks like a two-levelled tree from table roles in database - just
* like original Symfony roles are stored in security.yml
* @return array
*/
private function buildRolesTree()
{
$hierarchy = array();
// to avoid database error in cli mode
if ( php_sapi_name() === 'cli' )
return $hierarchy;
/*
$pathInfo = $this->container->get("request_stack")->getCurrentRequest()->getPathInfo();
dump($pathInfo );
die();
*/
/**
* checking if is admin area.
* $isAdmin is true if in admin area.
* if is admin it will load all role hierarchy or only one or two levels will be loaded
*
*/
$request = $this->container->get("request_stack")->getMasterRequest();
if(!$request) {
return array();
}
$pathInfo = $request->getPathInfo();
$adminPrefix = $this->container->getParameter("app.secured_area.route_prefix");
$isAdmin = false;
// preparing the app.secured_area.route_prefix to replace any url parameter as {_locale} to be (.*) as /{_locale}/admin to be /(.*)/admin
// preg_quote($adminPrefix) to escape $adminPrefix to be looks like /\{_locale\}\/admin
$adminPrefix = preg_replace("/{[^}]+}/", "(.*)", preg_replace( array('/\\\{/', '/\}/'), array('{', '}'), preg_quote($adminPrefix, '/')));
if(preg_match("/^($adminPrefix)/i", $pathInfo)){
$isAdmin = true;
$this->cacheFilePath .= DIRECTORY_SEPARATOR . "rawafed_security_bundle" . DIRECTORY_SEPARATOR . base64_encode($adminPrefix);
}else{
$this->cacheFilePath .= DIRECTORY_SEPARATOR . "rawafed_security_bundle" . DIRECTORY_SEPARATOR . "not_secured";
}
$this->cacheFilePath .= ".roles_hierarchy.cache.yml";
$em = $this->container->get('doctrine.orm.entity_manager');
$cacheFile = new ConfigCache( $this->cacheFilePath, $this->isDebugMode );
if($cacheFile->isFresh()) {
$yamlParser = new Parser();
$cacheFileContent = file_get_contents($this->cacheFilePath);
try {
$hierarchy = $yamlParser->parse($cacheFileContent);
return $hierarchy;
} catch (ParseException $e) {
}
}
/**
* if isAdmin will select all roles levels or it will load only two levels.
* there is no roles levels in manual roles
*/
$roles = $em->createQuery('SELECT r FROM RawafedSecurityBundle:Role r ' . ($isAdmin === false ? "WHERE r.lvl IN (0, 1) AND r.role NOT LIKE 'ROLE_ADMIN_%'" : "" ) . ' ORDER BY r.root, r.lft')->execute();
foreach($roles as $role) {
$roleName = $role->getRole();
if($role->getParent()) {
$parentRoleName = $role->getParent()->getRole();
if(!isset($hierarchy[$parentRoleName])) {
$hierarchy[$parentRoleName] = [];
}
$hierarchy[$parentRoleName][] = $roleName;
}
if(!isset($hierarchy[$roleName]) && (strpos($roleName, 'ROLE_ADMIN_') === FALSE) && ($roleName != 'ROLE_SUPER_ADMIN')) {
$hierarchy[$roleName] = array($roleName);
}
}
$dumper = new Dumper();
$rolesHierarchy = $dumper->dump($hierarchy, 10);
$cacheFile->write($rolesHierarchy);
return $hierarchy;
}
}