diff --git a/app/DoctrineMigrations/Version20120815091637.php b/app/DoctrineMigrations/Version20120815091637.php new file mode 100644 index 0000000..a35708a --- /dev/null +++ b/app/DoctrineMigrations/Version20120815091637.php @@ -0,0 +1,32 @@ +abortIf($this->connection->getDatabasePlatform()->getName() != "mysql"); + + $this->addSql("CREATE TABLE role_hierarchy (parent_role_id INT NOT NULL, child_role_id INT NOT NULL, INDEX IDX_AB8EFB72A44B56EA (parent_role_id), INDEX IDX_AB8EFB72B4B76AB7 (child_role_id), PRIMARY KEY(parent_role_id, child_role_id)) ENGINE = InnoDB"); + $this->addSql("ALTER TABLE role_hierarchy ADD CONSTRAINT FK_AB8EFB72A44B56EA FOREIGN KEY (parent_role_id) REFERENCES roles (id)"); + $this->addSql("ALTER TABLE role_hierarchy ADD CONSTRAINT FK_AB8EFB72B4B76AB7 FOREIGN KEY (child_role_id) REFERENCES roles (id)"); + $this->addSql("ALTER TABLE roles DROP admin, DROP superAdmin"); + } + + public function down(Schema $schema) + { + // this down() migration is autogenerated, please modify it to your needs + $this->abortIf($this->connection->getDatabasePlatform()->getName() != "mysql"); + + $this->addSql("DROP TABLE role_hierarchy"); + $this->addSql("ALTER TABLE roles ADD admin TINYINT(1) NOT NULL, ADD superAdmin TINYINT(1) NOT NULL"); + } +} diff --git a/src/KekRozsak/SecurityBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php b/src/KekRozsak/SecurityBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php new file mode 100644 index 0000000..59947ae --- /dev/null +++ b/src/KekRozsak/SecurityBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php @@ -0,0 +1,16 @@ +getDefinition('security.role_hierarchy'); + $definition->setClass('KekRozsak\SecurityBundle\Service\RoleHierarchy'); + $definition->setArguments(array(new Reference('doctrine'))); + } +} diff --git a/src/KekRozsak/SecurityBundle/Entity/Role.php b/src/KekRozsak/SecurityBundle/Entity/Role.php index 159e0c8..e92f31d 100644 --- a/src/KekRozsak/SecurityBundle/Entity/Role.php +++ b/src/KekRozsak/SecurityBundle/Entity/Role.php @@ -78,62 +78,6 @@ class Role implements RoleInterface return $this; } - /** - * @var boolean $admin - * @ORM\Column(type="boolean", nullable=false) - */ - protected $admin; - - /** - * Set admin - * - * @param boolean $admin - * @return Role - */ - public function setAdmin($admin) - { - $this->admin = $admin; - return $this; - } - - /** - * Get admin - * - * @return boolean - */ - public function isAdmin() - { - return $this->admin; - } - - /** - * @var boolean $superadmin - * @ORM\Column(type="boolean", nullable=false) - */ - protected $superAdmin; - - /** - * Set superadmin - * - * @param boolean $superadmin - * @return Role - */ - public function setSuperadmin($superadmin) - { - $this->superadmin = $superadmin; - return $this; - } - - /** - * Get superadmin - * - * @return boolean - */ - public function getSuperadmin() - { - return $this->superadmin; - } - /** * @var text description * @ORM\Column(type="string", length=150, nullable=true) @@ -199,5 +143,27 @@ class Role implements RoleInterface { return $this->shortDescription; } + + /** + * List of inherited Roles + * + * @ORM\ManyToMany(targetEntity="Role", fetch="LAZY") + * @ORM\JoinTable(name="role_hierarchy", joinColumns={ + * @ORM\JoinColumn(name="parent_role_id", referencedColumnName="id") + * }, inverseJoinColumns={ + * @ORM\JoinColumn(name="child_role_id", referencedColumnName="id") + * }) + */ + protected $inheritedRoles; + + /** + * Get all inherited roles + * + * @return Doctrine\Common\Collections\ArrayCollection + */ + public function getInheritedRoles() + { + return $this->inheritedRoles; + } } diff --git a/src/KekRozsak/SecurityBundle/Entity/User.php b/src/KekRozsak/SecurityBundle/Entity/User.php index 415ecde..f5f50ec 100644 --- a/src/KekRozsak/SecurityBundle/Entity/User.php +++ b/src/KekRozsak/SecurityBundle/Entity/User.php @@ -323,7 +323,18 @@ class User implements UserInterface, AdvancedUserInterface } /** - * Get all roles + * Get all roles as an ArrayCollection + * + * @return Doctrine\Common\Collections\ArrayCollection + */ + public function getRolesCollection() + { + return $this->roles; + } + + /** + * Get all roles, for UserInterface implementation. To get the + * collection, use getRolesCollection() instead * * @return array */ diff --git a/src/KekRozsak/SecurityBundle/KekRozsakSecurityBundle.php b/src/KekRozsak/SecurityBundle/KekRozsakSecurityBundle.php index 26a4099..e8d27b7 100644 --- a/src/KekRozsak/SecurityBundle/KekRozsakSecurityBundle.php +++ b/src/KekRozsak/SecurityBundle/KekRozsakSecurityBundle.php @@ -3,7 +3,15 @@ namespace KekRozsak\SecurityBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +use KekRozsak\SecurityBundle\DependencyInjection\Compiler\OverrideServiceCompilerPass; class KekRozsakSecurityBundle extends Bundle { + public function build(ContainerBuilder $container) + { + parent::build($container); + $container->addCompilerPass(new OverrideServiceCompilerPass()); + } } diff --git a/src/KekRozsak/SecurityBundle/Service/RoleHierarchy.php b/src/KekRozsak/SecurityBundle/Service/RoleHierarchy.php new file mode 100644 index 0000000..6105777 --- /dev/null +++ b/src/KekRozsak/SecurityBundle/Service/RoleHierarchy.php @@ -0,0 +1,57 @@ +hierarchy = array(); + $this->roleRepo = $doctrine->getRepository('KekRozsakSecurityBundle:Role'); + + $this->buildRoleMap(); + } + + public function getReachableRoles(array $roles) + { + $reachableRoles = array(); + foreach ($roles as $role) { + if (!isset($this->map[$role->getRole()])) { + continue; + } + + foreach ($this->map[$role->getRole()] as $r) { + if (($childRole = $this->roleRepo->findOneByName($r)) !== null) { + $reachableRoles[] = $childRole; + } + } + } + + return $reachableRoles; + } + + private function buildRoleMap() + { + $this->map = array(); + $roles = $this->roleRepo->findAll(); + foreach ($roles as $mainRole) { + $main = $mainRole->getRole(); + $this->map[$main] = array(); + foreach ($mainRole->getInheritedRoles() as $childRole) { + $this->map[$main][] = $childRole->getRole(); + // TODO: This is one-level only. Get as deep as possible. + // BEWARE OF RECURSIVE NESTING! + foreach ($childRole->getInheritedRoles() as $grandchildRole) { + $this->map[$main][] = $grandchildRole->getRole(); + } + } + } + } +}