#1180 -- Greatly improve user self-deletion/role management protections.
This commit is contained in:
parent
722dce7785
commit
e2c73c13fd
|
@ -94,12 +94,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/AzuraCast/azuracore.git",
|
||||
"reference": "ecf6362ae7ee8403f626b9fcfac0e6767f00378d"
|
||||
"reference": "ddfbbc6586b8d3cb4c72f8cf6edf678b9cadb6ba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/AzuraCast/azuracore/zipball/ecf6362ae7ee8403f626b9fcfac0e6767f00378d",
|
||||
"reference": "ecf6362ae7ee8403f626b9fcfac0e6767f00378d",
|
||||
"url": "https://api.github.com/repos/AzuraCast/azuracore/zipball/ddfbbc6586b8d3cb4c72f8cf6edf678b9cadb6ba",
|
||||
"reference": "ddfbbc6586b8d3cb4c72f8cf6edf678b9cadb6ba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -149,7 +149,7 @@
|
|||
}
|
||||
],
|
||||
"description": "A lightweight core application framework.",
|
||||
"time": "2019-02-05T17:08:40+00:00"
|
||||
"time": "2019-02-17T22:53:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "azuracast/azuraforms",
|
||||
|
|
|
@ -46,6 +46,7 @@ return function (\Azura\EventDispatcher $dispatcher)
|
|||
|
||||
// User-side tools
|
||||
new Command\ResetPassword,
|
||||
new Command\SetAdministrator,
|
||||
new Command\ListSettings,
|
||||
new Command\SetSetting,
|
||||
]);
|
||||
|
|
|
@ -5,69 +5,47 @@ $actions = \App\Acl::listPermissions();
|
|||
|
||||
$form_config = [
|
||||
'method' => 'post',
|
||||
'groups' => [
|
||||
'elements' => [
|
||||
|
||||
'basic_info' => [
|
||||
'elements' => [
|
||||
|
||||
'name' => [
|
||||
'text',
|
||||
[
|
||||
'label' => __('Role Name'),
|
||||
'class' => 'half-width',
|
||||
'required' => true,
|
||||
]
|
||||
],
|
||||
|
||||
],
|
||||
'name' => [
|
||||
'text',
|
||||
[
|
||||
'label' => __('Role Name'),
|
||||
'class' => 'half-width',
|
||||
'required' => true,
|
||||
]
|
||||
],
|
||||
|
||||
'grp_global' => [
|
||||
'legend' => __('System-Wide Permissions'),
|
||||
'elements' => [
|
||||
|
||||
'actions_global' => [
|
||||
'multiSelect',
|
||||
[
|
||||
'label' => __('Actions'),
|
||||
'choices' => $actions['global'],
|
||||
]
|
||||
],
|
||||
|
||||
],
|
||||
'actions_global' => [
|
||||
'multiSelect',
|
||||
[
|
||||
'label' => __('System-Wide Permissions'),
|
||||
'choices' => $actions['global'],
|
||||
'class' => 'permission-select',
|
||||
]
|
||||
],
|
||||
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($all_stations as $station) {
|
||||
$form_config['groups']['grp_station_' . $station['id']] = [
|
||||
'legend' => __('Per-Station').': '.$station['name'],
|
||||
'elements' => [
|
||||
|
||||
'actions_' . $station['id'] => [
|
||||
'multiSelect',
|
||||
[
|
||||
'label' => __('Actions'),
|
||||
'choices' => $actions['station'],
|
||||
]
|
||||
],
|
||||
|
||||
],
|
||||
$form_config['elements']['actions_' . $station['id']] = [
|
||||
'multiSelect',
|
||||
[
|
||||
'label' => __('Permissions for %s', $station['name']),
|
||||
'choices' => $actions['station'],
|
||||
'class' => 'permission-select',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$form_config['groups']['grp_submit'] = [
|
||||
'elements' => [
|
||||
'submit' => [
|
||||
'submit',
|
||||
[
|
||||
'type' => 'submit',
|
||||
'label' => __('Save Changes'),
|
||||
'class' => 'btn btn-lg btn-primary',
|
||||
]
|
||||
],
|
||||
],
|
||||
$form_config['elements']['submit'] = [
|
||||
'submit',
|
||||
[
|
||||
'type' => 'submit',
|
||||
'label' => __('Save Changes'),
|
||||
'class' => 'btn btn-lg btn-primary',
|
||||
]
|
||||
];
|
||||
|
||||
return $form_config;
|
||||
|
|
|
@ -263,27 +263,6 @@ return function (\Azura\Container $di)
|
|||
);
|
||||
};
|
||||
|
||||
$di[\App\Form\EntityForm::class] = function($di) {
|
||||
return new \App\Form\EntityForm(
|
||||
$di[\Doctrine\ORM\EntityManager::class],
|
||||
$di[\Symfony\Component\Serializer\Serializer::class],
|
||||
$di[\Symfony\Component\Validator\Validator\ValidatorInterface::class]
|
||||
);
|
||||
};
|
||||
|
||||
$di[\App\Form\StationForm::class] = function($di) {
|
||||
/** @var \Azura\Config $config */
|
||||
$config = $di[\Azura\Config::class];
|
||||
|
||||
return new \App\Form\StationForm(
|
||||
$di[\Doctrine\ORM\EntityManager::class],
|
||||
$di[\Symfony\Component\Serializer\Serializer::class],
|
||||
$di[\Symfony\Component\Validator\Validator\ValidatorInterface::class],
|
||||
$di[\App\Acl::class],
|
||||
$config->get('forms/station')
|
||||
);
|
||||
};
|
||||
|
||||
// Radio management
|
||||
$di->register(new \App\Provider\RadioProvider);
|
||||
|
||||
|
@ -299,6 +278,9 @@ return function (\Azura\Container $di)
|
|||
// Notifications
|
||||
$di->register(new \App\Provider\NotificationProvider);
|
||||
|
||||
// Forms
|
||||
$di->register(new \App\Provider\FormProvider);
|
||||
|
||||
// Controller groups
|
||||
$di->register(new \App\Provider\AdminProvider);
|
||||
$di->register(new \App\Provider\ApiProvider);
|
||||
|
|
81
src/Acl.php
81
src/Acl.php
|
@ -3,28 +3,28 @@ namespace App;
|
|||
|
||||
class Acl
|
||||
{
|
||||
const GLOBAL_ALL = 'administer all';
|
||||
const GLOBAL_VIEW = 'view administration';
|
||||
const GLOBAL_LOGS = 'view system logs';
|
||||
const GLOBAL_SETTINGS = 'administer settings';
|
||||
const GLOBAL_API_KEYS = 'administer api keys';
|
||||
const GLOBAL_USERS = 'administer user accounts';
|
||||
const GLOBAL_PERMISSIONS = 'administer permissions';
|
||||
const GLOBAL_STATIONS = 'administer stations';
|
||||
const GLOBAL_CUSTOM_FIELDS = 'administer custom fields';
|
||||
public const GLOBAL_ALL = 'administer all';
|
||||
public const GLOBAL_VIEW = 'view administration';
|
||||
public const GLOBAL_LOGS = 'view system logs';
|
||||
public const GLOBAL_SETTINGS = 'administer settings';
|
||||
public const GLOBAL_API_KEYS = 'administer api keys';
|
||||
public const GLOBAL_USERS = 'administer user accounts';
|
||||
public const GLOBAL_PERMISSIONS = 'administer permissions';
|
||||
public const GLOBAL_STATIONS = 'administer stations';
|
||||
public const GLOBAL_CUSTOM_FIELDS = 'administer custom fields';
|
||||
|
||||
const STATION_ALL = 'administer all';
|
||||
const STATION_VIEW = 'view station management';
|
||||
const STATION_REPORTS = 'view station reports';
|
||||
const STATION_LOGS = 'view station logs';
|
||||
const STATION_PROFILE = 'manage station profile';
|
||||
const STATION_BROADCASTING = 'manage station broadcasting';
|
||||
const STATION_STREAMERS = 'manage station streamers';
|
||||
const STATION_MOUNTS = 'manage station mounts';
|
||||
const STATION_REMOTES = 'manage station remotes';
|
||||
const STATION_MEDIA = 'manage station media';
|
||||
const STATION_AUTOMATION = 'manage station automation';
|
||||
const STATION_WEB_HOOKS = 'manage station web hooks';
|
||||
public const STATION_ALL = 'administer all';
|
||||
public const STATION_VIEW = 'view station management';
|
||||
public const STATION_REPORTS = 'view station reports';
|
||||
public const STATION_LOGS = 'view station logs';
|
||||
public const STATION_PROFILE = 'manage station profile';
|
||||
public const STATION_BROADCASTING = 'manage station broadcasting';
|
||||
public const STATION_STREAMERS = 'manage station streamers';
|
||||
public const STATION_MOUNTS = 'manage station mounts';
|
||||
public const STATION_REMOTES = 'manage station remotes';
|
||||
public const STATION_MEDIA = 'manage station media';
|
||||
public const STATION_AUTOMATION = 'manage station automation';
|
||||
public const STATION_WEB_HOOKS = 'manage station web hooks';
|
||||
|
||||
/** @var Entity\Repository\RolePermissionRepository */
|
||||
protected $permission_repo;
|
||||
|
@ -85,9 +85,10 @@ class Acl
|
|||
*
|
||||
* @param int|array $role_id
|
||||
* @param string|array $action
|
||||
* @param int|null $station_id
|
||||
* @return bool
|
||||
*/
|
||||
public function roleAllowed($role_id, $action, $station_id = null)
|
||||
public function roleAllowed($role_id, $action, $station_id = null): bool
|
||||
{
|
||||
// Iterate through an array of roles and return with the first "true" response, or "false" otherwise.
|
||||
if (\is_array($role_id)) {
|
||||
|
@ -114,17 +115,17 @@ class Acl
|
|||
if (!empty($this->_actions[$role_id])) {
|
||||
$role_actions = (array)$this->_actions[$role_id];
|
||||
|
||||
if (\in_array('administer all', (array)$role_actions['global'], true)) {
|
||||
if (\in_array(self::GLOBAL_ALL, (array)$role_actions['global'], true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($station_id !== null) {
|
||||
if (\in_array('administer stations', (array)$role_actions['global'], true)) {
|
||||
if (\in_array(self::GLOBAL_STATIONS, (array)$role_actions['global'], true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!empty($role_actions['stations'][$station_id])) {
|
||||
if (\in_array('administer all', $role_actions['stations'][$station_id], true)) {
|
||||
if (\in_array(self::STATION_ALL, $role_actions['stations'][$station_id], true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -147,7 +148,7 @@ class Acl
|
|||
* @throws Exception\NotLoggedIn
|
||||
* @throws Exception\PermissionDenied
|
||||
*/
|
||||
public function checkPermission(?Entity\User $user = null, $action, $station_id = null)
|
||||
public function checkPermission(?Entity\User $user = null, $action, $station_id = null): void
|
||||
{
|
||||
if (!($user instanceof Entity\User)) {
|
||||
throw new Exception\NotLoggedIn;
|
||||
|
@ -171,26 +172,26 @@ class Acl
|
|||
self::GLOBAL_ALL => __('All Permissions'),
|
||||
self::GLOBAL_VIEW => __('View Administration Page'),
|
||||
self::GLOBAL_LOGS => __('View System Logs'),
|
||||
self::GLOBAL_SETTINGS => sprintf(__('Administer %s'), __('Settings')),
|
||||
self::GLOBAL_API_KEYS => sprintf(__('Administer %s'), __('API Keys')),
|
||||
self::GLOBAL_USERS => sprintf(__('Administer %s'), __('Users')),
|
||||
self::GLOBAL_PERMISSIONS => sprintf(__('Administer %s'), __('Permissions')),
|
||||
self::GLOBAL_STATIONS => sprintf(__('Administer %s'), __('Stations')),
|
||||
self::GLOBAL_CUSTOM_FIELDS => sprintf(__('Administer %s'), __('Custom Fields')),
|
||||
self::GLOBAL_SETTINGS => __('Administer %s', __('Settings')),
|
||||
self::GLOBAL_API_KEYS => __('Administer %s', __('API Keys')),
|
||||
self::GLOBAL_USERS => __('Administer %s', __('Users')),
|
||||
self::GLOBAL_PERMISSIONS => __('Administer %s', __('Permissions')),
|
||||
self::GLOBAL_STATIONS => __('Administer %s', __('Stations')),
|
||||
self::GLOBAL_CUSTOM_FIELDS => __('Administer %s', __('Custom Fields')),
|
||||
],
|
||||
'station' => [
|
||||
self::STATION_ALL => __('All Permissions'),
|
||||
self::STATION_VIEW => __('View Station Page'),
|
||||
self::STATION_REPORTS => __('View Station Reports'),
|
||||
self::STATION_LOGS => __('View Station Logs'),
|
||||
self::STATION_PROFILE => sprintf(__('Manage Station %s'), __('Profile')),
|
||||
self::STATION_BROADCASTING => sprintf(__('Manage Station %s'), __('Broadcasting')),
|
||||
self::STATION_STREAMERS => sprintf(__('Manage Station %s'), __('Streamers')),
|
||||
self::STATION_MOUNTS => sprintf(__('Manage Station %s'), __('Mount Points')),
|
||||
self::STATION_REMOTES => sprintf(__('Manage Station %s'), __('Remote Relays')),
|
||||
self::STATION_MEDIA => sprintf(__('Manage Station %s'), __('Media')),
|
||||
self::STATION_AUTOMATION => sprintf(__('Manage Station %s'), __('Automation')),
|
||||
self::STATION_WEB_HOOKS => sprintf(__('Manage Station %s'), __('Web Hooks')),
|
||||
self::STATION_PROFILE => __('Manage Station %s', __('Profile')),
|
||||
self::STATION_BROADCASTING => __('Manage Station %s', __('Broadcasting')),
|
||||
self::STATION_STREAMERS => __('Manage Station %s', __('Streamers')),
|
||||
self::STATION_MOUNTS => __('Manage Station %s', __('Mount Points')),
|
||||
self::STATION_REMOTES => __('Manage Station %s', __('Remote Relays')),
|
||||
self::STATION_MEDIA => __('Manage Station %s', __('Media')),
|
||||
self::STATION_AUTOMATION => __('Manage Station %s', __('Automation')),
|
||||
self::STATION_WEB_HOOKS => __('Manage Station %s', __('Web Hooks')),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
|
|
@ -58,9 +58,9 @@ class ResetPassword extends CommandAbstract
|
|||
'',
|
||||
]);
|
||||
return 0;
|
||||
} else {
|
||||
$io->error('Account not found.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$io->error('Account not found.');
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
namespace App\Console\Command;
|
||||
|
||||
use App\Acl;
|
||||
use App\Entity;
|
||||
use Azura\Console\Command\CommandAbstract;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
class SetAdministrator extends CommandAbstract
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('azuracast:account:set-administrator')
|
||||
->setDescription('Set the account specified as a global administrator.')
|
||||
->addArgument(
|
||||
'email',
|
||||
InputArgument::REQUIRED,
|
||||
'The user\'s e-mail address.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$io->title('Set Administrator');
|
||||
|
||||
/** @var EntityManager $em */
|
||||
$em = $this->get(EntityManager::class);
|
||||
|
||||
$user_email = $input->getArgument('email');
|
||||
|
||||
$user = $em->getRepository(Entity\User::class)
|
||||
->findOneBy(['email' => $user_email ]);
|
||||
|
||||
if ($user instanceof Entity\User) {
|
||||
$admin_role = $em->getRepository(Entity\Role::class)
|
||||
->find(Entity\Role::SUPER_ADMINISTRATOR_ROLE_ID);
|
||||
|
||||
/** @var Entity\Repository\RolePermissionRepository $perms_repo */
|
||||
$perms_repo = $em->getRepository(Entity\RolePermission::class);
|
||||
|
||||
$perms_repo->setActionsForRole($admin_role, [
|
||||
'actions_global' => [
|
||||
Acl::GLOBAL_ALL,
|
||||
]
|
||||
]);
|
||||
|
||||
$user_roles = $user->getRoles();
|
||||
|
||||
if (!$user_roles->contains($admin_role)) {
|
||||
$user_roles->add($admin_role);
|
||||
}
|
||||
|
||||
$em->persist($user);
|
||||
$em->flush();
|
||||
|
||||
$io->text(__('The account associated with e-mail address "%s" has been set as an administrator', $user->getEmail()));
|
||||
$io->newLine();
|
||||
return 0;
|
||||
}
|
||||
|
||||
$io->error(__('Account not found.'));
|
||||
$io->newLine();
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Acl;
|
||||
use App\Form\PermissionsForm;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use App\Entity;
|
||||
use App\Http\Request;
|
||||
|
@ -13,24 +14,24 @@ class PermissionsController
|
|||
/** @var EntityManager */
|
||||
protected $em;
|
||||
|
||||
/** @var array */
|
||||
protected $actions;
|
||||
|
||||
/** @var array */
|
||||
protected $form_config;
|
||||
/** @var PermissionsForm */
|
||||
protected $form;
|
||||
|
||||
/** @var string */
|
||||
protected $csrf_namespace = 'admin_permissions';
|
||||
|
||||
/**
|
||||
* @param EntityManager $em
|
||||
* @param array $form_config
|
||||
* @param PermissionsForm $form
|
||||
*
|
||||
* @see \App\Provider\AdminProvider
|
||||
*/
|
||||
public function __construct(EntityManager $em, array $form_config)
|
||||
public function __construct(
|
||||
EntityManager $em,
|
||||
PermissionsForm $form)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->form_config = $form_config;
|
||||
$this->form = $form;
|
||||
}
|
||||
|
||||
public function indexAction(Request $request, Response $response): ResponseInterface
|
||||
|
@ -70,43 +71,18 @@ class PermissionsController
|
|||
/** @var \Azura\Doctrine\Repository $role_repo */
|
||||
$role_repo = $this->em->getRepository(Entity\Role::class);
|
||||
|
||||
/** @var Entity\Repository\RolePermissionRepository $permission_repo */
|
||||
$permission_repo = $this->em->getRepository(Entity\RolePermission::class);
|
||||
|
||||
$form = new \AzuraForms\Form($this->form_config);
|
||||
|
||||
if (!empty($id)) {
|
||||
$record = $role_repo->find($id);
|
||||
$record_info = $role_repo->toArray($record, true, true);
|
||||
|
||||
$actions = $permission_repo->getActionsForRole($record);
|
||||
|
||||
$form->populate(array_merge($record_info, $actions));
|
||||
} else {
|
||||
$record = null;
|
||||
}
|
||||
|
||||
if (!empty($_POST) && $form->isValid($_POST)) {
|
||||
$data = $form->getValues();
|
||||
|
||||
if (!($record instanceof Entity\Role)) {
|
||||
$record = new Entity\Role;
|
||||
}
|
||||
|
||||
$role_repo->fromArray($record, $data);
|
||||
|
||||
$this->em->persist($record);
|
||||
$this->em->flush();
|
||||
|
||||
$permission_repo->setActionsForRole($record, $data);
|
||||
$record = (null !== $id)
|
||||
? $role_repo->find((int)$id)
|
||||
: null;
|
||||
|
||||
if (false !== $this->form->process($request, $record)) {
|
||||
$request->getSession()->flash('<b>' . sprintf(($id) ? __('%s updated.') : __('%s added.'), __('Permission')) . '</b>', 'green');
|
||||
|
||||
return $response->withRedirect($request->getRouter()->named('admin:permissions:index'));
|
||||
}
|
||||
|
||||
return $request->getView()->renderToResponse($response, 'system/form_page', [
|
||||
'form' => $form,
|
||||
'form' => $this->form,
|
||||
'render_mode' => 'edit',
|
||||
'title' => sprintf(($id) ? __('Edit %s') : __('Add %s'), __('Permission')),
|
||||
]);
|
||||
|
|
|
@ -26,7 +26,7 @@ class StationsController
|
|||
public function __construct(Form\StationForm $station_form)
|
||||
{
|
||||
$this->station_form = $station_form;
|
||||
$this->record_repo = $station_form->getStationRepository();
|
||||
$this->record_repo = $station_form->getEntityRepository();
|
||||
}
|
||||
|
||||
public function __invoke(Request $request, Response $response): ResponseInterface
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Auth;
|
||||
use App\Form\UserForm;
|
||||
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use App\Entity;
|
||||
|
@ -14,14 +15,14 @@ class UsersController
|
|||
/** @var EntityManager */
|
||||
protected $em;
|
||||
|
||||
/** @var Entity\Repository\UserRepository */
|
||||
protected $record_repo;
|
||||
|
||||
/** @var Auth */
|
||||
protected $auth;
|
||||
|
||||
/** @var array */
|
||||
protected $form_config;
|
||||
|
||||
/** @var Entity\Repository\UserRepository */
|
||||
protected $record_repo;
|
||||
/** @var UserForm */
|
||||
protected $form;
|
||||
|
||||
/** @var string */
|
||||
protected $csrf_namespace = 'admin_users';
|
||||
|
@ -29,14 +30,18 @@ class UsersController
|
|||
/**
|
||||
* @param EntityManager $em
|
||||
* @param Auth $auth
|
||||
* @param array $form_config
|
||||
* @param UserForm $form
|
||||
*
|
||||
* @see \App\Provider\AdminProvider
|
||||
*/
|
||||
public function __construct(EntityManager $em, Auth $auth, array $form_config)
|
||||
public function __construct(
|
||||
EntityManager $em,
|
||||
Auth $auth,
|
||||
UserForm $form)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->auth = $auth;
|
||||
$this->form_config = $form_config;
|
||||
$this->form = $form;
|
||||
|
||||
$this->record_repo = $this->em->getRepository(Entity\User::class);
|
||||
}
|
||||
|
@ -55,42 +60,23 @@ class UsersController
|
|||
|
||||
public function editAction(Request $request, Response $response, $id = null): ResponseInterface
|
||||
{
|
||||
$form = new \AzuraForms\Form($this->form_config);
|
||||
$record = (null !== $id)
|
||||
? $this->record_repo->find((int)$id)
|
||||
: null;
|
||||
|
||||
if (!empty($id)) {
|
||||
$record = $this->record_repo->find((int)$id);
|
||||
$record_defaults = $this->record_repo->toArray($record, true, true);
|
||||
|
||||
unset($record_defaults['auth_password']);
|
||||
|
||||
$form->populate($record_defaults);
|
||||
} else {
|
||||
$record = null;
|
||||
}
|
||||
|
||||
if (!empty($_POST) && $form->isValid($_POST)) {
|
||||
$data = $form->getValues();
|
||||
|
||||
if (!($record instanceof Entity\User)) {
|
||||
$record = new Entity\User;
|
||||
}
|
||||
|
||||
$this->record_repo->fromArray($record, $data);
|
||||
|
||||
try {
|
||||
$this->em->persist($record);
|
||||
$this->em->flush();
|
||||
|
||||
$request->getSession()->flash(sprintf(($id) ? __('%s updated.') : __('%s added.'), __('User')), 'green');
|
||||
try {
|
||||
if (false !== $this->form->process($request, $record)) {
|
||||
$request->getSession()->flash(sprintf(($id) ? __('%s updated.') : __('%s added.'), __('User')),
|
||||
'green');
|
||||
|
||||
return $response->withRedirect($request->getRouter()->named('admin:users:index'));
|
||||
} catch(UniqueConstraintViolationException $e) {
|
||||
$request->getSession()->flash(__('Another user already exists with this e-mail address. Please update the e-mail address.'), 'red');
|
||||
}
|
||||
} catch(UniqueConstraintViolationException $e) {
|
||||
$request->getSession()->flash(__('Another user already exists with this e-mail address. Please update the e-mail address.'), 'red');
|
||||
}
|
||||
|
||||
return $request->getView()->renderToResponse($response, 'system/form_page', [
|
||||
'form' => $form,
|
||||
'form' => $this->form,
|
||||
'render_mode' => 'edit',
|
||||
'title' => sprintf(($id) ? __('Edit %s') : __('Add %s'), __('User'))
|
||||
]);
|
||||
|
@ -102,14 +88,17 @@ class UsersController
|
|||
|
||||
$user = $this->record_repo->find((int)$id);
|
||||
|
||||
if ($user instanceof Entity\User) {
|
||||
$current_user = $request->getUser();
|
||||
|
||||
if ($user === $current_user) {
|
||||
$request->getSession()->flash('<b>'.__('You cannot delete your own account.').'</b>', 'red');
|
||||
} elseif ($user instanceof Entity\User) {
|
||||
$this->em->remove($user);
|
||||
$this->em->flush();
|
||||
|
||||
$request->getSession()->flash('<b>' . __('%s deleted.', __('User')) . '</b>', 'green');
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
$request->getSession()->flash('<b>' . __('%s deleted.', __('User')) . '</b>', 'green');
|
||||
|
||||
return $response->withRedirect($request->getRouter()->named('admin:users:index'));
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||
*/
|
||||
class Role implements \JsonSerializable
|
||||
{
|
||||
public const SUPER_ADMINISTRATOR_ROLE_ID = 1;
|
||||
|
||||
use Traits\TruncateStrings;
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
namespace App\Form;
|
||||
|
||||
use App\Http\Request;
|
||||
use Azura\Doctrine\Repository;
|
||||
use Azura\Normalizer\DoctrineEntityNormalizer;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
|
@ -46,14 +48,6 @@ class EntityForm extends \AzuraForms\Form
|
|||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
*/
|
||||
public function getEntityManager(): EntityManager
|
||||
{
|
||||
return $this->em;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
@ -70,6 +64,26 @@ class EntityForm extends \AzuraForms\Form
|
|||
$this->entityClass = $entityClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
*/
|
||||
public function getEntityManager(): EntityManager
|
||||
{
|
||||
return $this->em;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Repository
|
||||
*/
|
||||
public function getEntityRepository(): Repository
|
||||
{
|
||||
if (null === $this->entityClass) {
|
||||
throw new \Azura\Exception('Entity class name is not specified.');
|
||||
}
|
||||
|
||||
return $this->em->getRepository($this->entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param object|null $record
|
||||
|
@ -77,7 +91,11 @@ class EntityForm extends \AzuraForms\Form
|
|||
*/
|
||||
public function process(Request $request, $record = null)
|
||||
{
|
||||
if (!($record instanceof $this->entityClass)) {
|
||||
if (null === $this->entityClass) {
|
||||
throw new \Azura\Exception('Entity class name is not specified.');
|
||||
}
|
||||
|
||||
if (null !== $record && !($record instanceof $this->entityClass)) {
|
||||
throw new \InvalidArgumentException(sprintf('Record must be an instance of %s.', $this->entityClass));
|
||||
}
|
||||
|
||||
|
@ -94,9 +112,25 @@ class EntityForm extends \AzuraForms\Form
|
|||
|
||||
$errors = $this->validator->validate($record);
|
||||
if (count($errors) > 0) {
|
||||
$e = new \App\Exception\Validation((string)$errors);
|
||||
$e->setDetailedErrors($errors);
|
||||
throw $e;
|
||||
$other_errors = [];
|
||||
foreach($errors as $error) {
|
||||
/** @var ConstraintViolation $error */
|
||||
$field_name = $error->getPropertyPath();
|
||||
|
||||
if (isset($this->fields[$field_name])) {
|
||||
$this->fields[$field_name]->addError($error->getMessage());
|
||||
} else {
|
||||
$other_errors[] = $error;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($other_errors) > 0) {
|
||||
$e = new \App\Exception\Validation((string)$errors);
|
||||
$e->setDetailedErrors($errors);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->em->persist($record);
|
||||
|
@ -110,9 +144,9 @@ class EntityForm extends \AzuraForms\Form
|
|||
/**
|
||||
* @param $record
|
||||
* @param array $context
|
||||
* @return array|bool|float|int|mixed|string
|
||||
* @return array
|
||||
*/
|
||||
protected function _normalizeRecord($record, array $context = [])
|
||||
protected function _normalizeRecord($record, array $context = []): array
|
||||
{
|
||||
return $this->serializer->normalize($record, null, array_merge($context, [
|
||||
DoctrineEntityNormalizer::NORMALIZE_TO_IDENTIFIERS => true,
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity;
|
||||
use App\Http\Request;
|
||||
use Azura\Doctrine\Repository;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
class PermissionsForm extends EntityForm
|
||||
{
|
||||
/** @var Entity\Repository\RolePermissionRepository */
|
||||
protected $permissions_repo;
|
||||
|
||||
/** @var bool */
|
||||
protected $set_permissions = true;
|
||||
|
||||
/**
|
||||
* @param EntityManager $em
|
||||
* @param Serializer $serializer
|
||||
* @param ValidatorInterface $validator
|
||||
* @param array $form_config
|
||||
*/
|
||||
public function __construct(
|
||||
EntityManager $em,
|
||||
Serializer $serializer,
|
||||
ValidatorInterface $validator,
|
||||
array $form_config)
|
||||
{
|
||||
parent::__construct($em, $serializer, $validator, $form_config);
|
||||
|
||||
$this->entityClass = Entity\Role::class;
|
||||
$this->permissions_repo = $em->getRepository(Entity\RolePermission::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getEntityRepository(): Repository
|
||||
{
|
||||
return $this->permissions_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function process(Request $request, $record = null)
|
||||
{
|
||||
if ($record instanceof Entity\Role && Entity\Role::SUPER_ADMINISTRATOR_ROLE_ID === $record->getId()) {
|
||||
$this->set_permissions = false;
|
||||
|
||||
foreach($this->fields as $field_id => $field) {
|
||||
$attrs = $field->getAttributes();
|
||||
if (isset($attrs['class']) && strpos($attrs['class'], 'permission-select') !== false) {
|
||||
unset($this->fields[$field_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::process($request, $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function _denormalizeToRecord($data, $record = null, array $context = []): object
|
||||
{
|
||||
$record = parent::_denormalizeToRecord($data, $record, $context);
|
||||
|
||||
if ($this->set_permissions) {
|
||||
$this->em->persist($record);
|
||||
$this->em->flush();
|
||||
|
||||
$this->permissions_repo->setActionsForRole($record, $data);
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function _normalizeRecord($record, array $context = []): array
|
||||
{
|
||||
$data = parent::_normalizeRecord($record, $context);
|
||||
|
||||
if ($this->set_permissions) {
|
||||
$actions = $this->permissions_repo->getActionsForRole($record);
|
||||
return array_merge($data, $actions);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ namespace App\Form;
|
|||
use App\Acl;
|
||||
use App\Entity;
|
||||
use App\Http\Request;
|
||||
use Azura\Doctrine\Repository;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
@ -41,9 +42,9 @@ class StationForm extends EntityForm
|
|||
}
|
||||
|
||||
/**
|
||||
* @return Entity\Repository\StationRepository
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getStationRepository(): Entity\Repository\StationRepository
|
||||
public function getEntityRepository(): Repository
|
||||
{
|
||||
return $this->station_repo;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity;
|
||||
use App\Http\Request;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
class UserForm extends EntityForm
|
||||
{
|
||||
/**
|
||||
* @param EntityManager $em
|
||||
* @param Serializer $serializer
|
||||
* @param ValidatorInterface $validator
|
||||
* @param array $form_config
|
||||
*/
|
||||
public function __construct(
|
||||
EntityManager $em,
|
||||
Serializer $serializer,
|
||||
ValidatorInterface $validator,
|
||||
array $form_config)
|
||||
{
|
||||
parent::__construct($em, $serializer, $validator, $form_config);
|
||||
|
||||
$this->entityClass = Entity\User::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function process(Request $request, $record = null)
|
||||
{
|
||||
// Check for administrative permissions and hide admin fields otherwise.
|
||||
$user = $request->getUser();
|
||||
|
||||
if ($record instanceof Entity\User && $record->getId() === $user->getId()) {
|
||||
unset($this->fields['roles']);
|
||||
}
|
||||
|
||||
return parent::process($request, $record);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
namespace App\Provider;
|
||||
|
||||
use App\Controller\Admin;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use Pimple\Container;
|
||||
use App\Entity;
|
||||
|
@ -15,7 +16,7 @@ class AdminProvider implements ServiceProviderInterface
|
|||
$config = $di[\Azura\Config::class];
|
||||
|
||||
return new Admin\ApiController(
|
||||
$di[\Doctrine\ORM\EntityManager::class],
|
||||
$di[EntityManager::class],
|
||||
$config->get('forms/api_key')
|
||||
);
|
||||
};
|
||||
|
@ -35,7 +36,7 @@ class AdminProvider implements ServiceProviderInterface
|
|||
$config = $di[\Azura\Config::class];
|
||||
|
||||
return new Admin\CustomFieldsController(
|
||||
$di[\Doctrine\ORM\EntityManager::class],
|
||||
$di[EntityManager::class],
|
||||
$config->get('forms/custom_field')
|
||||
);
|
||||
};
|
||||
|
@ -50,25 +51,14 @@ class AdminProvider implements ServiceProviderInterface
|
|||
|
||||
$di[Admin\LogsController::class] = function($di) {
|
||||
return new Admin\LogsController(
|
||||
$di[\Doctrine\ORM\EntityManager::class]
|
||||
$di[EntityManager::class]
|
||||
);
|
||||
};
|
||||
|
||||
$di[Admin\PermissionsController::class] = function($di) {
|
||||
/** @var \Azura\Config $config */
|
||||
$config = $di[\Azura\Config::class];
|
||||
|
||||
/** @var \Doctrine\ORM\EntityManager $em */
|
||||
$em = $di[\Doctrine\ORM\EntityManager::class];
|
||||
|
||||
/** @var Entity\Repository\StationRepository $stations_repo */
|
||||
$stations_repo = $em->getRepository(Entity\Station::class);
|
||||
|
||||
return new Admin\PermissionsController(
|
||||
$em,
|
||||
$config->get('forms/role', [
|
||||
'all_stations' => $stations_repo->fetchArray(),
|
||||
])
|
||||
$di[EntityManager::class],
|
||||
$di[\App\Form\PermissionsForm::class]
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -93,7 +83,7 @@ class AdminProvider implements ServiceProviderInterface
|
|||
$config = $di[\Azura\Config::class];
|
||||
|
||||
return new Admin\Stations\CloneController(
|
||||
$di[\Doctrine\ORM\EntityManager::class],
|
||||
$di[EntityManager::class],
|
||||
$di[\Azura\Cache::class],
|
||||
$di[\App\Radio\Configuration::class],
|
||||
$config->get('forms/station_clone')
|
||||
|
@ -101,21 +91,10 @@ class AdminProvider implements ServiceProviderInterface
|
|||
};
|
||||
|
||||
$di[Admin\UsersController::class] = function($di) {
|
||||
/** @var \Azura\Config $config */
|
||||
$config = $di[\Azura\Config::class];
|
||||
|
||||
/** @var \Doctrine\ORM\EntityManager $em */
|
||||
$em = $di[\Doctrine\ORM\EntityManager::class];
|
||||
|
||||
/** @var \Azura\Doctrine\Repository $role_repo */
|
||||
$role_repo = $em->getRepository(Entity\Role::class);
|
||||
|
||||
return new Admin\UsersController(
|
||||
$di[\Doctrine\ORM\EntityManager::class],
|
||||
$di[EntityManager::class],
|
||||
$di[\App\Auth::class],
|
||||
$config->get('forms/user', [
|
||||
'roles' => $role_repo->fetchSelect()
|
||||
])
|
||||
$di[\App\Form\UserForm::class]
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
namespace App\Provider;
|
||||
|
||||
use App\Form;
|
||||
use App\Entity;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use Pimple\Container;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
class FormProvider implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $di)
|
||||
{
|
||||
$di[Form\EntityForm::class] = function($di) {
|
||||
return new Form\EntityForm(
|
||||
$di[EntityManager::class],
|
||||
$di[Serializer::class],
|
||||
$di[ValidatorInterface::class]
|
||||
);
|
||||
};
|
||||
|
||||
$di[Form\PermissionsForm::class] = function($di) {
|
||||
/** @var \Azura\Config $config */
|
||||
$config = $di[\Azura\Config::class];
|
||||
|
||||
/** @var \Doctrine\ORM\EntityManager $em */
|
||||
$em = $di[EntityManager::class];
|
||||
|
||||
/** @var Entity\Repository\StationRepository $stations_repo */
|
||||
$stations_repo = $em->getRepository(Entity\Station::class);
|
||||
|
||||
return new Form\PermissionsForm(
|
||||
$di[EntityManager::class],
|
||||
$di[Serializer::class],
|
||||
$di[ValidatorInterface::class],
|
||||
$config->get('forms/role', [
|
||||
'all_stations' => $stations_repo->fetchArray(),
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
$di[Form\StationForm::class] = function($di) {
|
||||
/** @var \Azura\Config $config */
|
||||
$config = $di[\Azura\Config::class];
|
||||
|
||||
return new Form\StationForm(
|
||||
$di[EntityManager::class],
|
||||
$di[Serializer::class],
|
||||
$di[ValidatorInterface::class],
|
||||
$di[\App\Acl::class],
|
||||
$config->get('forms/station')
|
||||
);
|
||||
};
|
||||
|
||||
$di[Form\UserForm::class] = function($di) {
|
||||
/** @var \Azura\Config $config */
|
||||
$config = $di[\Azura\Config::class];
|
||||
|
||||
/** @var \Doctrine\ORM\EntityManager $em */
|
||||
$em = $di[EntityManager::class];
|
||||
|
||||
/** @var \Azura\Doctrine\Repository $role_repo */
|
||||
$role_repo = $em->getRepository(Entity\Role::class);
|
||||
|
||||
return new Form\UserForm(
|
||||
$di[EntityManager::class],
|
||||
$di[Serializer::class],
|
||||
$di[ValidatorInterface::class],
|
||||
$config->get('forms/user', [
|
||||
'roles' => $role_repo->fetchSelect()
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -13,17 +13,15 @@
|
|||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<colgroup>
|
||||
<col width="20%">
|
||||
<col width="20%">
|
||||
<col width="30%">
|
||||
<col width="30%">
|
||||
<col width="50%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?=__('Actions') ?></th>
|
||||
<th><?=__('Role Name') ?></th>
|
||||
<th><?=__('System-Wide Permissions') ?></th>
|
||||
<th><?=__('Per-Station Permissions') ?></th>
|
||||
<th><?=__('Permissions') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -39,15 +37,15 @@
|
|||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<big><?=$this->e($role['name']) ?></big>
|
||||
<div class="typography-subheading"><?=$this->e($role['name']) ?></div>
|
||||
</td>
|
||||
<td>
|
||||
<?=implode($role['permissions_global'], ', ') ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php foreach($role['permissions_station'] as $station_name => $station_perms): ?>
|
||||
<div><b><?=$this->e($station_name) ?></b>: <?=implode($station_perms, ', ') ?></div>
|
||||
<?php endforeach; ?>
|
||||
<?php if (!empty($role['permissions_global'])): ?>
|
||||
<div><b><?=__('Global') ?>:</b> <?=implode($role['permissions_global'], ', ') ?></div>
|
||||
<?php endif; ?>
|
||||
<?php foreach($role['permissions_station'] as $station_name => $station_perms): ?>
|
||||
<div><b><?=$this->e($station_name) ?></b>: <?=implode($station_perms, ', ') ?></div>
|
||||
<?php endforeach; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
<table class="table table-striped">
|
||||
<colgroup>
|
||||
<col width="30%">
|
||||
<col width="40%">
|
||||
<col width="30%">
|
||||
<col width="35%">
|
||||
<col width="35%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -33,19 +33,23 @@
|
|||
<a class="btn btn-sm btn-primary" href="<?=$router->named('admin:users:impersonate', ['id' => $user_row->getId(), 'csrf' => $csrf]) ?>"><?=__('Log In') ?></a>
|
||||
<?php endif; ?>
|
||||
<a class="btn btn-sm btn-default" href="<?=$router->named('admin:users:edit', ['id' => $user_row->getId()]) ?>"><?=__('Edit') ?></a>
|
||||
<?php if ($user_row->getId() !== $user->getId()): ?>
|
||||
<a class="btn btn-sm btn-danger" data-confirm-title="<?=$this->e(__('Delete user "%s"?', $user_row->getEmail())) ?>" href="<?=$router->named('admin:users:delete', ['id' => $user_row->getId(), 'csrf' => $csrf]) ?>"><?=__('Delete') ?></a>
|
||||
<?php else: ?>
|
||||
<a class="btn btn-sm btn-danger disabled" href=""><?=__('Delete') ?></a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?=$this->mailto($user_row->getEmail()) ?> <?php if ($user_row->getId() === $user->getId()): ?><?=__('(You)') ?><?php endif; ?></td>
|
||||
<td>
|
||||
<small>
|
||||
<?php
|
||||
if (count($user_row->getRoles()) > 0)
|
||||
{
|
||||
foreach($user_row->getRoles() as $role)
|
||||
echo '<div>'.$this->e($role->getName()).'</div>';
|
||||
}
|
||||
?>
|
||||
</small>
|
||||
<div class="text-lg-left"><?=$this->e($user_row->getName()) ?></div>
|
||||
<div>
|
||||
<?=$this->mailto($user_row->getEmail()) ?>
|
||||
<?php if ($user_row->getId() === $user->getId()): ?><?=__('(You)') ?><?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<?php foreach($user_row->getRoles() as $role): ?>
|
||||
<div><?=$this->e($role->getName()) ?></div>
|
||||
<?php endforeach; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
|
Loading…
Reference in New Issue