From cf69f61cf7ee7ebfb7944dacf674defaa31522c9 Mon Sep 17 00:00:00 2001 From: Buster Silver Date: Fri, 7 Oct 2016 15:57:42 -0500 Subject: [PATCH] Migrate permissions system to use hard-coded action lists, and update the Permissions management page to solely allow role management. --- app/library/App/Acl.php | 4 +- app/models/Entity/Action.php | 40 ------ app/models/Entity/Role.php | 6 +- .../{RoleHasAction.php => RolePermission.php} | 74 +++-------- .../Migration/Version20161007195027.php | 47 +++++++ app/modules/admin/config/actions.conf.php | 30 +++++ app/modules/admin/config/dashboard.conf.php | 2 +- app/modules/admin/config/forms/role.conf.php | 51 +++++--- .../controllers/PermissionsController.php | 72 ++++------- app/modules/admin/routes.php | 18 +-- .../views/scripts/permissions/index.phtml | 122 +++++++----------- app/modules/stations/config/actions.conf.php | 18 +++ 12 files changed, 232 insertions(+), 252 deletions(-) delete mode 100644 app/models/Entity/Action.php rename app/models/Entity/{RoleHasAction.php => RolePermission.php} (52%) create mode 100644 app/models/Migration/Version20161007195027.php create mode 100644 app/modules/admin/config/actions.conf.php create mode 100644 app/modules/stations/config/actions.conf.php diff --git a/app/library/App/Acl.php b/app/library/App/Acl.php index 06fc41f0c..c9232c448 100644 --- a/app/library/App/Acl.php +++ b/app/library/App/Acl.php @@ -5,9 +5,9 @@ namespace App; +use Entity\RolePermission; use Entity\User; use Entity\Role; -use Entity\RoleHasAction; class Acl { @@ -32,7 +32,7 @@ class Acl protected function init() { if (null === $this->_actions) - $this->_actions = $this->_em->getRepository(RoleHasAction::class)->getActionsForAllRoles(); + $this->_actions = $this->_em->getRepository(RolePermission::class)->getActionsForAllRoles(); } /** diff --git a/app/models/Entity/Action.php b/app/models/Entity/Action.php deleted file mode 100644 index 7571c0e7b..000000000 --- a/app/models/Entity/Action.php +++ /dev/null @@ -1,40 +0,0 @@ -is_global = false; - $this->has_role = new ArrayCollection; - } - - /** - * @Column(name="id", type="integer") - * @Id - * @GeneratedValue(strategy="IDENTITY") - */ - protected $id; - - /** @Column(name="name", type="string", length=100, nullable=true) */ - protected $name; - - /** @Column(name="is_global", type="boolean") */ - protected $is_global; - - /** @OneToMany(targetEntity="Entity\RoleHasAction", mappedBy="action") */ - protected $has_role; -} - -use App\Doctrine\Repository; - -class ActionRepository extends Repository -{ - -} \ No newline at end of file diff --git a/app/models/Entity/Role.php b/app/models/Entity/Role.php index 1c02455e3..7e090a04e 100644 --- a/app/models/Entity/Role.php +++ b/app/models/Entity/Role.php @@ -12,7 +12,7 @@ class Role extends \App\Doctrine\Entity public function __construct() { $this->users = new ArrayCollection; - $this->has_action = new ArrayCollection; + $this->permissions = new ArrayCollection; } /** @@ -28,6 +28,6 @@ class Role extends \App\Doctrine\Entity /** @ManyToMany(targetEntity="User", mappedBy="roles")*/ protected $users; - /** @OneToMany(targetEntity="Entity\RoleHasAction", mappedBy="role") */ - protected $has_action; + /** @OneToMany(targetEntity="Entity\RolePermission", mappedBy="role") */ + protected $permissions; } \ No newline at end of file diff --git a/app/models/Entity/RoleHasAction.php b/app/models/Entity/RolePermission.php similarity index 52% rename from app/models/Entity/RoleHasAction.php rename to app/models/Entity/RolePermission.php index a6556a867..f519e7766 100644 --- a/app/models/Entity/RoleHasAction.php +++ b/app/models/Entity/RolePermission.php @@ -4,12 +4,12 @@ namespace Entity; use Doctrine\Common\Collections\ArrayCollection; /** - * @Table(name="role_has_actions", uniqueConstraints={ - * @UniqueConstraint(name="role_action_unique_idx", columns={"role_id","action_id","station_id"}) + * @Table(name="role_permissions", uniqueConstraints={ + * @UniqueConstraint(name="role_permission_unique_idx", columns={"role_id","action_name","station_id"}) * }) - * @Entity(repositoryClass="RoleHasActionRepository") + * @Entity(repositoryClass="RolePermissionRepository") */ -class RoleHasAction extends \App\Doctrine\Entity +class RolePermission extends \App\Doctrine\Entity { /** * @Column(name="id", type="integer") @@ -22,23 +22,15 @@ class RoleHasAction extends \App\Doctrine\Entity protected $role_id; /** - * @ManyToOne(targetEntity="Role", inversedBy="has_actions") + * @ManyToOne(targetEntity="Role", inversedBy="permissions") * @JoinColumns({ * @JoinColumn(name="role_id", referencedColumnName="id", onDelete="CASCADE") * }) */ protected $role; - /** @Column(name="action_id", type="integer") */ - protected $action_id; - - /** - * @ManyToOne(targetEntity="Action", inversedBy="has_roles") - * @JoinColumns({ - * @JoinColumn(name="action_id", referencedColumnName="id", onDelete="CASCADE") - * }) - */ - protected $action; + /** @Column(name="action_name", type="string", length=50, nullable=false) */ + protected $action_name; /** @Column(name="station_id", type="integer", nullable=true) */ protected $station_id; @@ -54,54 +46,24 @@ class RoleHasAction extends \App\Doctrine\Entity use App\Doctrine\Repository; -class RoleHasActionRepository extends Repository +class RolePermissionRepository extends Repository { public function getActionsForAllRoles() { - $role_has_actions = $this->_em->createQuery('SELECT rha, a FROM '.$this->_entityName.' rha JOIN rha.action a') - ->getArrayResult(); + $all_permissions = $this->fetchArray(); $roles = []; - - foreach($role_has_actions as $row) + foreach($all_permissions as $row) { if ($row['station_id']) - $roles[$row['role_id']]['stations'][$row['station_id']][] = $row['action']['name']; + $roles[$row['role_id']]['stations'][$row['station_id']][] = $row['action_name']; else - $roles[$row['role_id']]['global'][] = $row['action']['name']; + $roles[$row['role_id']]['global'][] = $row['action_name']; } return $roles; } - public function getSelectableActions() - { - $actions = []; - - $all_actions = $this->_em->getRepository(Action::class)->fetchArray(); - $all_stations = $this->_em->getRepository(Station::class)->fetchArray(); - - foreach($all_actions as $action) - { - if ($action['is_global']) - { - $actions['global'][$action['id']] = $action['name']; - } - else - { - foreach($all_stations as $station) - { - if (!isset($actions['stations'][$station['id']])) - $actions['stations'][$station['id']] = ['name' => $station['name'], 'actions' => []]; - - $actions['stations'][$station['id']]['actions'][$action['id']] = $action['name']; - } - } - } - - return $actions; - } - public function getActionsForRole(Role $role) { $role_has_action = $this->findBy(['role_id' => $role->id]); @@ -110,9 +72,9 @@ class RoleHasActionRepository extends Repository foreach($role_has_action as $row) { if ($row['station_id']) - $result['actions_'.$row['station_id']][] = $row['action_id']; + $result['actions_'.$row['station_id']][] = $row['action_name']; else - $result['actions_global'][] = $row['action_id']; + $result['actions_global'][] = $row['action_name']; } return $result; @@ -120,7 +82,7 @@ class RoleHasActionRepository extends Repository public function setActionsForRole(Role $role, $post_values) { - $this->_em->createQuery('DELETE FROM '.$this->_entityName.' rha WHERE rha.role_id = :role_id') + $this->_em->createQuery('DELETE FROM '.$this->_entityName.' rp WHERE rp.role_id = :role_id') ->setParameter('role_id', $role->id) ->execute(); @@ -131,17 +93,17 @@ class RoleHasActionRepository extends Repository if ($post_key_action !== 'actions' || empty($post_value)) continue; - foreach((array)$post_value as $action_id) + foreach((array)$post_value as $action_name) { $record_info = [ 'role_id' => $role->id, - 'action_id' => $action_id + 'action_name' => $action_name, ]; if ($post_key_id !== 'global') $record_info['station_id'] = $post_key_id; - $record = new RoleHasAction; + $record = new RolePermission; $record->fromArray($this->_em, $record_info); $this->_em->persist($record); diff --git a/app/models/Migration/Version20161007195027.php b/app/models/Migration/Version20161007195027.php new file mode 100644 index 000000000..6e660697d --- /dev/null +++ b/app/models/Migration/Version20161007195027.php @@ -0,0 +1,47 @@ +abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('CREATE TABLE role_permissions (id INT AUTO_INCREMENT NOT NULL, role_id INT NOT NULL, station_id INT DEFAULT NULL, action_name VARCHAR(50) NOT NULL, INDEX IDX_1FBA94E6D60322AC (role_id), INDEX IDX_1FBA94E621BDB235 (station_id), UNIQUE INDEX role_permission_unique_idx (role_id, action_name, station_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB'); + $this->addSql('ALTER TABLE role_permissions ADD CONSTRAINT FK_1FBA94E6D60322AC FOREIGN KEY (role_id) REFERENCES role (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE role_permissions ADD CONSTRAINT FK_1FBA94E621BDB235 FOREIGN KEY (station_id) REFERENCES station (id) ON DELETE CASCADE'); + + $this->addSql('INSERT INTO role_permissions (role_id, action_name, station_id) SELECT rha.role_id, a.name, rha.station_id FROM role_has_actions AS rha INNER JOIN action AS a ON rha.action_id = a.id'); + + $this->addSql('ALTER TABLE role_has_actions DROP FOREIGN KEY FK_50EEC1BD9D32F035'); + $this->addSql('DROP TABLE action'); + $this->addSql('DROP TABLE role_has_actions'); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + // this down() migration is auto-generated, please modify it to your needs + $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('CREATE TABLE action (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(100) DEFAULT NULL COLLATE utf8_unicode_ci, is_global TINYINT(1) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB'); + $this->addSql('CREATE TABLE role_has_actions (id INT AUTO_INCREMENT NOT NULL, station_id INT DEFAULT NULL, action_id INT NOT NULL, role_id INT NOT NULL, UNIQUE INDEX role_action_unique_idx (role_id, action_id, station_id), INDEX IDX_50EEC1BDD60322AC (role_id), INDEX IDX_50EEC1BD21BDB235 (station_id), INDEX IDX_50EEC1BD9D32F035 (action_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB'); + $this->addSql('ALTER TABLE role_has_actions ADD CONSTRAINT FK_50EEC1BD21BDB235 FOREIGN KEY (station_id) REFERENCES station (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE role_has_actions ADD CONSTRAINT FK_50EEC1BD9D32F035 FOREIGN KEY (action_id) REFERENCES action (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE role_has_actions ADD CONSTRAINT FK_50EEC1BDD60322AC FOREIGN KEY (role_id) REFERENCES role (id) ON DELETE CASCADE'); + $this->addSql('DROP TABLE role_permissions'); + } +} diff --git a/app/modules/admin/config/actions.conf.php b/app/modules/admin/config/actions.conf.php new file mode 100644 index 000000000..f4823e5c8 --- /dev/null +++ b/app/modules/admin/config/actions.conf.php @@ -0,0 +1,30 @@ + [ + 'administer all', + 'view administration', + 'administer settings', + 'administer api keys', + 'administer user accounts', + 'administer permissions', + 'administer stations', + ], + 'station' => [ + + ], +]; + +/* + ['view station management', 0], + ['view station reports', 0], + ['manage station profile', 0], + ['manage station broadcasting', 0], + ['manage station streamers', 0], + ['manage station media', 0], + ['manage station automation', 0], +]; +*/ \ No newline at end of file diff --git a/app/modules/admin/config/dashboard.conf.php b/app/modules/admin/config/dashboard.conf.php index 143eac1b6..0680e8f9f 100644 --- a/app/modules/admin/config/dashboard.conf.php +++ b/app/modules/admin/config/dashboard.conf.php @@ -27,7 +27,7 @@ return [ 'icon' => 'fa fa-user', 'permission' => 'administer user accounts', ], - _('Roles and Permissions') => [ + _('Permissions') => [ 'url' => 'admin:permissions:index', 'icon' => 'fa fa-key', 'permission' => 'administer permissions', diff --git a/app/modules/admin/config/forms/role.conf.php b/app/modules/admin/config/forms/role.conf.php index c6f7f67a2..1ca383d7c 100644 --- a/app/modules/admin/config/forms/role.conf.php +++ b/app/modules/admin/config/forms/role.conf.php @@ -3,10 +3,28 @@ * Edit Role Form */ +/** @var array */ +$module_config = $di['module_config']; + +$actions = []; + +foreach($module_config as $module_name => $config_row) +{ + $module_actions = $config_row->actions->toArray(); + + if (!empty($module_actions)) + { + foreach($module_actions['global'] as $action_name) + $actions['global'][$action_name] = $action_name; + + foreach($module_actions['station'] as $action_name) + $actions['station'][$action_name] = $action_name; + } +} + /** @var \Doctrine\ORM\EntityManager $em */ $em = $di['em']; - -$all_actions = $em->getRepository(\Entity\RoleHasAction::class)->getSelectableActions(); +$all_stations = $em->getRepository(\Entity\Station::class)->fetchArray(); $form_config = [ 'method' => 'post', @@ -23,30 +41,31 @@ $form_config = [ ], ], - ], -]; -$form_config['groups']['grp_global'] = [ - 'legend' => _('System-Wide Permissions'), - 'elements' => [ + 'grp_global' => [ + 'legend' => _('System-Wide Permissions'), + 'elements' => [ - 'actions_global' => ['multiCheckbox', [ - 'label' => _('Actions'), - 'multiOptions' => $all_actions['global'], - ]], + 'actions_global' => ['multiCheckbox', [ + 'label' => _('Actions'), + 'multiOptions' => $actions['global'], + ]], + + ], + ], ], ]; -foreach($all_actions['stations'] as $station_id => $station_info) +foreach($all_stations as $station) { - $form_config['groups']['grp_station_'.$station_id] = [ - 'legend' => $station_info['name'], + $form_config['groups']['grp_station_'.$station['id']] = [ + 'legend' => $station['name'], 'elements' => [ - 'actions_'.$station_id => ['multiCheckbox', [ + 'actions_'.$station['id'] => ['multiCheckbox', [ 'label' => _('Actions'), - 'multiOptions' => $station_info['actions'], + 'multiOptions' => $actions['station'], ]], ], diff --git a/app/modules/admin/controllers/PermissionsController.php b/app/modules/admin/controllers/PermissionsController.php index 613c2244c..41b755095 100644 --- a/app/modules/admin/controllers/PermissionsController.php +++ b/app/modules/admin/controllers/PermissionsController.php @@ -1,9 +1,8 @@ view->actions = $this->em->getRepository(Action::class)->fetchArray(false, 'name'); - $this->view->roles = $this->em->getRepository(Role::class)->fetchArray(false, 'name'); - } - - public function editactionAction() - { - $form = new \App\Form($this->current_module_config->forms->action->toArray()); - - if ($this->hasParam('id')) + $all_roles = $this->em->createQuery('SELECT r, rp, s FROM Entity\Role r LEFT JOIN r.users u LEFT JOIN r.permissions rp LEFT JOIN rp.station s ORDER BY r.id ASC') + ->getArrayResult(); + + $roles = []; + + foreach($all_roles as $role) { - $record = $this->em->getRepository(Action::class)->find($this->getParam('id')); - $form->setDefaults($record->toArray($this->em)); + $role['permissions_global'] = []; + $role['permissions_station'] = []; + + foreach($role['permissions'] as $permission) + { + if ($permission['station']) + $role['permissions_station'][$permission['station']['name']][] = $permission['action_name']; + else + $role['permissions_global'][] = $permission['action_name']; + } + + $roles[] = $role; } - if(!empty($_POST) && $form->isValid($_POST)) - { - $data = $form->getValues(); - - if (!($record instanceof Action)) - $record = new Action; - - $record->fromArray($this->em, $data); - - $this->em->persist($record); - $this->em->flush(); - - $this->alert(_('Record updated.'), 'green'); - return $this->redirectFromHere(array('action' => 'index', 'id' => NULL, 'csrf' => NULL)); - } - - return $this->renderForm($form, 'edit', _('Edit Record')); - } - - public function deleteactionAction() - { - $action = $this->em->getRepository(Action::class)->find($this->getParam('id')); - if ($action) - $action->delete(); - - $this->alert(_('Record deleted.'), 'green'); - return $this->redirectFromHere(array('action' => 'index', 'id' => NULL, 'csrf' => NULL)); + $this->view->roles = $roles; } - public function rolemembersAction() + public function membersAction() { $roles = $this->em->createQuery('SELECT r, a, u FROM Entity\Role r LEFT JOIN r.actions a LEFT JOIN r.users u') ->getArrayResult(); @@ -65,7 +45,7 @@ class PermissionsController extends BaseController $this->view->roles = $roles; } - public function editroleAction() + public function editAction() { $form = new \App\Form($this->current_module_config->forms->role->toArray()); @@ -74,7 +54,7 @@ class PermissionsController extends BaseController $record = $this->em->getRepository(Role::class)->find($this->getParam('id')); $record_info = $record->toArray($this->em, true, true); - $actions = $this->em->getRepository(RoleHasAction::class)->getActionsForRole($record); + $actions = $this->em->getRepository(RolePermission::class)->getActionsForRole($record); $form->setDefaults(array_merge($record_info, $actions)); } @@ -91,7 +71,7 @@ class PermissionsController extends BaseController $this->em->persist($record); $this->em->flush(); - $this->em->getRepository(RoleHasAction::class)->setActionsForRole($record, $data); + $this->em->getRepository(RolePermission::class)->setActionsForRole($record, $data); $this->alert(''._('Record updated.').'', 'green'); return $this->redirectFromHere(array('action' => 'index', 'id' => NULL, 'csrf' => NULL)); @@ -100,7 +80,7 @@ class PermissionsController extends BaseController return $this->renderForm($form, 'edit', _('Edit Record')); } - public function deleteroleAction() + public function deleteAction() { $record = $this->em->getRepository(Role::class)->find($this->getParam('id')); if ($record instanceof Role) diff --git a/app/modules/admin/routes.php b/app/modules/admin/routes.php index acd3124fe..eb47a0e71 100644 --- a/app/modules/admin/routes.php +++ b/app/modules/admin/routes.php @@ -31,20 +31,14 @@ $app->group('/admin', function() { $this->get('', 'admin:permissions:index') ->setName('admin:permissions:index'); - $this->map(['GET', 'POST'], '/role/edit[/{id}]', 'admin:permissions:editrole') - ->setName('admin:permissions:editrole'); + $this->map(['GET', 'POST'], '/edit[/{id}]', 'admin:permissions:edit') + ->setName('admin:permissions:edit'); - $this->get('/role/delete/{id}', 'admin:permissions:deleterole') - ->setName('admin:permissions:deleterole'); + $this->get('/delete/{id}', 'admin:permissions:delete') + ->setName('admin:permissions:delete'); - $this->get('/role/members/{id}', 'admin:permissions:rolemembers') - ->setName('admin:permissions:rolemembers'); - - $this->map(['GET', 'POST'], '/action/edit[/{id}]', 'admin:permissions:editaction') - ->setName('admin:permissions:editaction'); - - $this->get('/action/delete/{id}', 'admin:permissions:deleteaction') - ->setName('admin:permissions:deleteaction'); + $this->get('/members/{id}', 'admin:permissions:members') + ->setName('admin:permissions:members'); }); diff --git a/app/modules/admin/views/scripts/permissions/index.phtml b/app/modules/admin/views/scripts/permissions/index.phtml index c5cdef33d..11d2ab933 100644 --- a/app/modules/admin/views/scripts/permissions/index.phtml +++ b/app/modules/admin/views/scripts/permissions/index.phtml @@ -4,83 +4,53 @@

-
-
-
-
-

- -
-
- - - - - - - - - - - - - - - - - - - -
 
- - - - - - - e($role['name']) ?>
-
-
+
+
+

+
-
-
-
-

- -
-
- - - - - - - - - - - - - - - - - +
+
 
- - - - e($action['name']) ?>
- - - - - - -
+ + + + + + + + + + + + + + + + + + + + + -
+ + + + + + + + + e($role['name']) ?> + + + + $station_perms): ?> +
:
-
-
-
+ + + + +
\ No newline at end of file diff --git a/app/modules/stations/config/actions.conf.php b/app/modules/stations/config/actions.conf.php new file mode 100644 index 000000000..3409ba717 --- /dev/null +++ b/app/modules/stations/config/actions.conf.php @@ -0,0 +1,18 @@ + [ + ], + 'station' => [ + 'view station management', + 'view station reports', + 'manage station profile', + 'manage station broadcasting', + 'manage station streamers', + 'manage station media', + 'manage station automation', + ], +]; \ No newline at end of file