Move Roles/Permissions component to Vue.
This commit is contained in:
parent
91424496b2
commit
dd07f982b5
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
/** @var array $all_stations */
|
||||
|
||||
$form_config = [
|
||||
'method' => 'post',
|
||||
'elements' => [
|
||||
|
||||
'name' => [
|
||||
'text',
|
||||
[
|
||||
'label' => __('Role Name'),
|
||||
'class' => 'half-width',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
|
||||
'actions_global' => [
|
||||
'multiSelect',
|
||||
[
|
||||
'label' => __('System-Wide Permissions'),
|
||||
'choices' => $actions['global'],
|
||||
'class' => 'permission-select',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($all_stations as $station) {
|
||||
$form_config['elements']['actions_' . $station['id']] = [
|
||||
'multiSelect',
|
||||
[
|
||||
'label' => __('Permissions for %s', $station['name']),
|
||||
'choices' => $actions['station'],
|
||||
'class' => 'permission-select',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$form_config['elements']['submit'] = [
|
||||
'submit',
|
||||
[
|
||||
'type' => 'submit',
|
||||
'label' => __('Save Changes'),
|
||||
'class' => 'btn btn-lg btn-primary',
|
||||
],
|
||||
];
|
||||
|
||||
return $form_config;
|
|
@ -57,7 +57,7 @@ return function (App\Event\BuildAdminMenu $e) {
|
|||
'permission' => Acl::GLOBAL_ALL,
|
||||
],
|
||||
'permissions' => [
|
||||
'label' => __('Permissions'),
|
||||
'label' => __('Roles & Permissions'),
|
||||
'url' => (string)$router->named('admin:permissions:index'),
|
||||
'permission' => Acl::GLOBAL_ALL,
|
||||
],
|
||||
|
|
|
@ -148,26 +148,9 @@ return static function (RouteCollectorProxy $app) {
|
|||
}
|
||||
)->add(new Middleware\Permissions(Acl::GLOBAL_LOGS));
|
||||
|
||||
$group->group(
|
||||
'/permissions',
|
||||
function (RouteCollectorProxy $group) {
|
||||
$group->get('', Controller\Admin\PermissionsController::class . ':indexAction')
|
||||
->setName('admin:permissions:index');
|
||||
|
||||
$group->map(
|
||||
['GET', 'POST'],
|
||||
'/edit/{id}',
|
||||
Controller\Admin\PermissionsController::class . ':editAction'
|
||||
)
|
||||
->setName('admin:permissions:edit');
|
||||
|
||||
$group->map(['GET', 'POST'], '/add', Controller\Admin\PermissionsController::class . ':editAction')
|
||||
->setName('admin:permissions:add');
|
||||
|
||||
$group->get('/delete/{id}/{csrf}', Controller\Admin\PermissionsController::class . ':deleteAction')
|
||||
->setName('admin:permissions:delete');
|
||||
}
|
||||
)->add(new Middleware\Permissions(Acl::GLOBAL_ALL));
|
||||
$group->get('/permissions', Controller\Admin\PermissionsAction::class)
|
||||
->setName('admin:permissions:index')
|
||||
->add(new Middleware\Permissions(Acl::GLOBAL_ALL));
|
||||
|
||||
$group->get('/relays', Controller\Admin\RelaysController::class)
|
||||
->setName('admin:relays:index')
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
<template>
|
||||
<div>
|
||||
<b-card no-body>
|
||||
<b-card-header header-bg-variant="primary-dark">
|
||||
<h2 class="card-title" key="lang_title" v-translate>Roles & Permissions</h2>
|
||||
</b-card-header>
|
||||
|
||||
<info-card>
|
||||
<p class="card-text">
|
||||
<translate key="lang_card_info">AzuraCast uses a role-based access control system. Roles are given permissions to certain sections of the site, then users are assigned into those roles.</translate>
|
||||
</p>
|
||||
</info-card>
|
||||
|
||||
<b-card-body body-class="card-padding-sm">
|
||||
<b-button variant="outline-primary" @click.prevent="doCreate">
|
||||
<icon icon="add"></icon>
|
||||
<translate key="lang_add_btn">Add Role</translate>
|
||||
</b-button>
|
||||
</b-card-body>
|
||||
|
||||
<data-table ref="datatable" id="permissions" :fields="fields" :show-toolbar="false" :api-url="listUrl">
|
||||
<template #cell(permissions)="row">
|
||||
<div v-if="row.item.permissions.global.length > 0">
|
||||
<translate key="lang_permissions_global">Global</translate>:
|
||||
{{ getGlobalPermissionNames(row.item.permissions.global).join(', ') }}
|
||||
</div>
|
||||
<div v-for="(permissions, stationId) in row.item.permissions.station" :key="stationId">
|
||||
<b>{{ getStationName(stationId) }}</b>:
|
||||
{{ getStationPermissionNames(permissions).join(', ') }}
|
||||
</div>
|
||||
</template>
|
||||
<template #cell(actions)="row">
|
||||
<b-button-group size="sm">
|
||||
<b-button size="sm" variant="primary" @click.prevent="doEdit(row.item.links.self)">
|
||||
<translate key="lang_btn_edit">Edit</translate>
|
||||
</b-button>
|
||||
<b-button v-if="row.item.id !== 1" size="sm" variant="danger"
|
||||
@click.prevent="doDelete(row.item.links.self)">
|
||||
<translate key="lang_btn_delete">Delete</translate>
|
||||
</b-button>
|
||||
</b-button-group>
|
||||
</template>
|
||||
</data-table>
|
||||
</b-card>
|
||||
|
||||
<edit-modal ref="editModal" :create-url="listUrl" :station-permissions="stationPermissions" :stations="stations"
|
||||
:global-permissions="globalPermissions" @relist="relist"></edit-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataTable from '~/components/Common/DataTable';
|
||||
import EditModal from './Permissions/EditModal';
|
||||
import Icon from '~/components/Common/Icon';
|
||||
import InfoCard from '~/components/Common/InfoCard';
|
||||
import handleAxiosError from '~/functions/handleAxiosError';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
name: 'AdminPermissions',
|
||||
components: {InfoCard, Icon, EditModal, DataTable},
|
||||
props: {
|
||||
listUrl: String,
|
||||
stations: Array,
|
||||
globalPermissions: Array,
|
||||
stationPermissions: Array
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fields: [
|
||||
{key: 'name', isRowHeader: true, label: this.$gettext('Role Name'), sortable: false},
|
||||
{key: 'permissions', label: this.$gettext('Permissions'), sortable: false},
|
||||
{key: 'actions', label: this.$gettext('Actions'), sortable: false, class: 'shrink'}
|
||||
]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getGlobalPermissionNames(permissions) {
|
||||
return _.filter(_.map(permissions, (permission) => {
|
||||
return _.get(this.globalPermissions, permission, null);
|
||||
}));
|
||||
},
|
||||
getStationPermissionNames(permissions) {
|
||||
return _.filter(_.map(permissions, (permission) => {
|
||||
return _.get(this.stationPermissions, permission, null);
|
||||
}));
|
||||
},
|
||||
getStationName(stationId) {
|
||||
return _.get(this.stations, stationId, null);
|
||||
},
|
||||
relist() {
|
||||
this.$refs.datatable.refresh();
|
||||
},
|
||||
doCreate() {
|
||||
this.$refs.editModal.create();
|
||||
},
|
||||
doEdit(url) {
|
||||
this.$refs.editModal.edit(url);
|
||||
},
|
||||
doDelete(url) {
|
||||
let buttonText = this.$gettext('Delete');
|
||||
let buttonConfirmText = this.$gettext('Delete Role?');
|
||||
|
||||
Swal.fire({
|
||||
title: buttonConfirmText,
|
||||
confirmButtonText: buttonText,
|
||||
confirmButtonColor: '#e64942',
|
||||
showCancelButton: true,
|
||||
focusCancel: true
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
this.axios.delete(url).then((resp) => {
|
||||
notify('<b>' + resp.data.message + '</b>', 'success');
|
||||
|
||||
this.relist();
|
||||
}).catch((err) => {
|
||||
handleAxiosError(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,116 @@
|
|||
<template>
|
||||
<b-modal size="lg" id="edit_modal" ref="modal" :title="langTitle" :busy="loading">
|
||||
<b-overlay variant="card" :show="loading">
|
||||
<b-alert variant="danger" :show="error != null">{{ error }}</b-alert>
|
||||
|
||||
<b-form class="form" @submit.prevent="doSubmit">
|
||||
<b-tabs content-class="mt-3">
|
||||
<admin-permissions-global-form :form="$v.form" :global-permissions="globalPermissions">
|
||||
</admin-permissions-global-form>
|
||||
|
||||
<admin-permissions-station-form :form="$v.form" :stations="stations"
|
||||
:station-permissions="stationPermissions">
|
||||
</admin-permissions-station-form>
|
||||
</b-tabs>
|
||||
|
||||
<invisible-submit-button/>
|
||||
</b-form>
|
||||
</b-overlay>
|
||||
<template #modal-footer>
|
||||
<b-button variant="default" type="button" @click="close">
|
||||
<translate key="lang_btn_close">Close</translate>
|
||||
</b-button>
|
||||
<b-button variant="primary" type="submit" @click="doSubmit" :disabled="$v.form.$invalid">
|
||||
<translate key="lang_btn_save_changes">Save Changes</translate>
|
||||
</b-button>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {validationMixin} from 'vuelidate';
|
||||
import {required} from 'vuelidate/dist/validators.min.js';
|
||||
import InvisibleSubmitButton from '~/components/Common/InvisibleSubmitButton';
|
||||
import BaseEditModal from '~/components/Common/BaseEditModal';
|
||||
import AdminPermissionsGlobalForm from "./Form/GlobalForm";
|
||||
import AdminPermissionsStationForm from "~/components/Admin/Permissions/Form/StationForm";
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
name: 'AdminPermissionsEditModal',
|
||||
components: {AdminPermissionsStationForm, AdminPermissionsGlobalForm, InvisibleSubmitButton},
|
||||
mixins: [validationMixin, BaseEditModal],
|
||||
props: {
|
||||
stations: Object,
|
||||
globalPermissions: Object,
|
||||
stationPermissions: Object
|
||||
},
|
||||
computed: {
|
||||
langTitle() {
|
||||
return this.isEditMode
|
||||
? this.$gettext('Edit Role')
|
||||
: this.$gettext('Add Role');
|
||||
}
|
||||
},
|
||||
validations() {
|
||||
return {
|
||||
form: {
|
||||
'name': {required},
|
||||
'permissions': {
|
||||
'global': {},
|
||||
'station': {
|
||||
$each: {
|
||||
'station_id': {},
|
||||
'permissions': {},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
resetForm() {
|
||||
this.form = {
|
||||
'name': '',
|
||||
'permissions': {
|
||||
'global': [],
|
||||
'station': [],
|
||||
}
|
||||
};
|
||||
},
|
||||
populateForm (data) {
|
||||
this.form.name = data.name;
|
||||
this.form.permissions.global = data.permissions.global;
|
||||
this.form.permissions.station = _.map(data.permissions.station, (permissions, stationId) => {
|
||||
return {
|
||||
'station_id': stationId,
|
||||
'permissions': permissions
|
||||
};
|
||||
});
|
||||
},
|
||||
buildSubmitRequest () {
|
||||
let form = {
|
||||
name: this.form.name,
|
||||
permissions: {
|
||||
global: this.form.permissions.global,
|
||||
station: {}
|
||||
}
|
||||
};
|
||||
|
||||
_.forEach(this.form.permissions.station, (row) => {
|
||||
form.permissions.station[row.station_id] = row.permissions;
|
||||
});
|
||||
|
||||
return {
|
||||
method: (this.isEditMode)
|
||||
? 'PUT'
|
||||
: 'POST',
|
||||
url: (this.isEditMode)
|
||||
? this.editUrl
|
||||
: this.createUrl,
|
||||
data: form
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<b-tab :title="langTabTitle" active>
|
||||
<b-form-group>
|
||||
<b-row>
|
||||
<b-wrapped-form-group class="col-md-12" id="edit_form_name" :field="form.name">
|
||||
<template #label>
|
||||
<translate key="lang_form_name">Role Name</translate>
|
||||
</template>
|
||||
</b-wrapped-form-group>
|
||||
|
||||
<b-wrapped-form-group class="col-md-12" id="edit_form_global_permissions"
|
||||
:field="form.permissions.global">
|
||||
<template #label>
|
||||
<translate key="lang_form_global_permissions">Global Permissions</translate>
|
||||
</template>
|
||||
<template #description>
|
||||
<translate key="lang_form_global_permissions_desc">Users with this role will have these permissions across the entire installation.</translate>
|
||||
</template>
|
||||
<template #default="props">
|
||||
<b-form-checkbox-group :id="props.id" :options="globalPermissionOptions"
|
||||
v-model="props.field.$model">
|
||||
</b-form-checkbox-group>
|
||||
</template>
|
||||
</b-wrapped-form-group>
|
||||
</b-row>
|
||||
</b-form-group>
|
||||
</b-tab>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
name: 'AdminPermissionsGlobalForm',
|
||||
components: {BWrappedFormGroup},
|
||||
props: {
|
||||
form: Object,
|
||||
globalPermissions: Object
|
||||
},
|
||||
computed: {
|
||||
langTabTitle() {
|
||||
return this.$gettext('Global Permissions');
|
||||
},
|
||||
globalPermissionOptions() {
|
||||
return _.map(this.globalPermissions, (permissionName, permissionKey) => {
|
||||
return {
|
||||
text: permissionName,
|
||||
value: permissionKey
|
||||
};
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<b-tab :title="langTabTitle">
|
||||
<b-card v-for="(row, index) in form.permissions.station.$each.$iter" :key="index" class="mb-3" no-body>
|
||||
<div class="card-header bg-primary-dark d-flex align-items-center">
|
||||
<div class="flex-fill">
|
||||
<h2 class="card-title">
|
||||
{{ getStationName(row.station_id.$model) }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
<b-button size="sm" variant="outline-light" class="py-2 pr-0" @click.prevent="remove(index)">
|
||||
<icon icon="remove"></icon>
|
||||
<translate key="lang_btn_remove">Remove</translate>
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
<b-card-body>
|
||||
<b-form-group>
|
||||
<b-row>
|
||||
<b-wrapped-form-group class="col-md-12" :id="'edit_form_station_permissions_'+row.station_id.$model"
|
||||
:field="row.permissions">
|
||||
<template #label>
|
||||
<translate key="lang_form_station_permission">Station Permissions</translate>
|
||||
</template>
|
||||
<template #description>
|
||||
<translate key="lang_form_station_permission_desc">Users with this role will have these permissions for this single station.</translate>
|
||||
</template>
|
||||
<template #default="props">
|
||||
<b-form-checkbox-group :id="props.id" :options="stationPermissionOptions"
|
||||
v-model="props.field.$model">
|
||||
</b-form-checkbox-group>
|
||||
</template>
|
||||
</b-wrapped-form-group>
|
||||
</b-row>
|
||||
</b-form-group>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
|
||||
<b-button-group v-if="hasRemainingStations">
|
||||
<b-dropdown size="sm" variant="outline-primary">
|
||||
<template #button-content>
|
||||
<translate key="lang_btn_add_station">Add Station</translate>
|
||||
</template>
|
||||
<b-dropdown-item-button v-for="(stationName, stationId) in remainingStations" :key="stationId"
|
||||
@click="add(stationId)">{{ stationName }}</b-dropdown-item-button>
|
||||
</b-dropdown>
|
||||
</b-button-group>
|
||||
</b-tab>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
|
||||
import Icon from "~/components/Common/Icon";
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
name: 'AdminPermissionsStationForm',
|
||||
components: {BWrappedFormGroup, Icon},
|
||||
props: {
|
||||
form: Object,
|
||||
stations: Object,
|
||||
stationPermissions: Object
|
||||
},
|
||||
computed: {
|
||||
langTabTitle() {
|
||||
return this.$gettext('Station Permissions');
|
||||
},
|
||||
stationPermissionOptions() {
|
||||
return _.map(this.stationPermissions, (permissionName, permissionKey) => {
|
||||
return {
|
||||
text: permissionName,
|
||||
value: permissionKey
|
||||
};
|
||||
})
|
||||
},
|
||||
remainingStations() {
|
||||
return _.pickBy(this.stations, (stationName, stationId) => {
|
||||
return !_.find(this.form.permissions.station.$model, {'station_id': stationId});
|
||||
});
|
||||
},
|
||||
hasRemainingStations() {
|
||||
return !_.isEmpty(this.remainingStations);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getStationName(stationId) {
|
||||
return _.get(this.stations, stationId, null);
|
||||
},
|
||||
remove (index) {
|
||||
this.form.permissions.station.$model.splice(index, 1);
|
||||
},
|
||||
add(stationId) {
|
||||
this.form.permissions.station.$model.push({
|
||||
'station_id': stationId,
|
||||
'permissions': []
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,19 @@
|
|||
import '~/base.js';
|
||||
import '~/vendor/bootstrapVue.js';
|
||||
|
||||
import Vue
|
||||
from 'vue';
|
||||
|
||||
import AdminPermissions
|
||||
from '~/components/Admin/Permissions.vue';
|
||||
|
||||
export default function (el, props) {
|
||||
return new Vue({
|
||||
el: el,
|
||||
render: (createElement) => {
|
||||
return createElement(AdminPermissions, {
|
||||
props: props
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -9,6 +9,7 @@ module.exports = {
|
|||
Dashboard: '~/pages/Dashboard.js',
|
||||
AdminAuditLog: '~/pages/Admin/AuditLog.js',
|
||||
AdminBranding: '~/pages/Admin/Branding.js',
|
||||
AdminPermissions: '~/pages/Admin/Permissions.js',
|
||||
AdminStorageLocations: '~/pages/Admin/StorageLocations.js',
|
||||
PublicFullPlayer: '~/pages/Public/FullPlayer.js',
|
||||
PublicHistory: '~/pages/Public/History.js',
|
||||
|
|
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Console\Command\Users;
|
||||
|
||||
use App\Acl;
|
||||
use App\Console\Command\CommandAbstract;
|
||||
use App\Entity;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
@ -24,36 +23,22 @@ class SetAdministratorCommand extends CommandAbstract
|
|||
->findOneBy(['email' => $email]);
|
||||
|
||||
if ($user instanceof Entity\User) {
|
||||
$admin_role = $em->getRepository(Entity\Role::class)
|
||||
->find(Entity\Role::SUPER_ADMINISTRATOR_ROLE_ID);
|
||||
|
||||
if (null === $admin_role) {
|
||||
$io->error('Administrator role not found.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$perms_repo->setActionsForRole(
|
||||
$admin_role,
|
||||
[
|
||||
'actions_global' => [
|
||||
Acl::GLOBAL_ALL,
|
||||
],
|
||||
]
|
||||
);
|
||||
$adminRole = $perms_repo->ensureSuperAdministratorRole();
|
||||
|
||||
$user_roles = $user->getRoles();
|
||||
|
||||
if (!$user_roles->contains($admin_role)) {
|
||||
$user_roles->add($admin_role);
|
||||
if (!$user_roles->contains($adminRole)) {
|
||||
$user_roles->add($adminRole);
|
||||
}
|
||||
|
||||
$em->persist($user);
|
||||
$em->flush();
|
||||
|
||||
$io->text(__(
|
||||
'The account associated with e-mail address "%s" has been set as an administrator',
|
||||
$user->getEmail()
|
||||
));
|
||||
$io->text(
|
||||
__(
|
||||
'The account associated with e-mail address "%s" has been set as an administrator',
|
||||
$user->getEmail()
|
||||
)
|
||||
);
|
||||
$io->newLine();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Entity\Repository\StationRepository;
|
||||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class PermissionsAction
|
||||
{
|
||||
public function __invoke(
|
||||
ServerRequest $request,
|
||||
Response $response,
|
||||
StationRepository $stationRepo
|
||||
): ResponseInterface {
|
||||
$router = $request->getRouter();
|
||||
|
||||
$actions = $request->getAcl()->listPermissions();
|
||||
|
||||
return $request->getView()->renderToResponse(
|
||||
$response,
|
||||
'system/vue',
|
||||
[
|
||||
'title' => __('Roles & Permissions'),
|
||||
'id' => 'admin-permissions',
|
||||
'component' => 'Vue_AdminPermissions',
|
||||
'props' => [
|
||||
'listUrl' => (string)$router->fromHere('api:admin:roles'),
|
||||
'stations' => $stationRepo->fetchSelect(),
|
||||
'globalPermissions' => $actions['global'],
|
||||
'stationPermissions' => $actions['station'],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Form\PermissionsForm;
|
||||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\Session\Flash;
|
||||
use DI\FactoryInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class PermissionsController extends AbstractAdminCrudController
|
||||
{
|
||||
public function __construct(
|
||||
FactoryInterface $factory
|
||||
) {
|
||||
parent::__construct($factory->make(PermissionsForm::class));
|
||||
|
||||
$this->csrf_namespace = 'admin_permissions';
|
||||
}
|
||||
|
||||
public function indexAction(ServerRequest $request, Response $response): ResponseInterface
|
||||
{
|
||||
$all_roles = $this->em->createQuery(
|
||||
<<<'DQL'
|
||||
SELECT r, rp, s
|
||||
FROM App\Entity\Role r
|
||||
LEFT JOIN r.users u
|
||||
LEFT JOIN r.permissions rp
|
||||
LEFT JOIN rp.station s
|
||||
ORDER BY r.id ASC
|
||||
DQL
|
||||
)->getArrayResult();
|
||||
|
||||
$roles = [];
|
||||
|
||||
$actions = $request->getAcl()->listPermissions();
|
||||
|
||||
foreach ($all_roles as $role) {
|
||||
$role['permissions_global'] = [];
|
||||
$role['permissions_station'] = [];
|
||||
|
||||
foreach ($role['permissions'] as $permission) {
|
||||
if ($permission['station']) {
|
||||
// phpcs:disable Generic.Files.LineLength
|
||||
$role['permissions_station'][$permission['station']['name']][] = $actions['station'][$permission['action_name']];
|
||||
// phpcs:enable
|
||||
} else {
|
||||
$role['permissions_global'][] = $actions['global'][$permission['action_name']];
|
||||
}
|
||||
}
|
||||
|
||||
$roles[] = $role;
|
||||
}
|
||||
|
||||
return $request->getView()->renderToResponse($response, 'admin/permissions/index', [
|
||||
'roles' => $roles,
|
||||
'csrf' => $request->getCsrf()->generate($this->csrf_namespace),
|
||||
]);
|
||||
}
|
||||
|
||||
public function editAction(ServerRequest $request, Response $response, int $id = null): ResponseInterface
|
||||
{
|
||||
if (false !== $this->doEdit($request, $id)) {
|
||||
$request->getFlash()->addMessage(
|
||||
'<b>' . ($id ? __('Permission updated.') : __('Permission added.')) . '</b>',
|
||||
Flash::SUCCESS
|
||||
);
|
||||
return $response->withRedirect((string)$request->getRouter()->named('admin:permissions:index'));
|
||||
}
|
||||
|
||||
return $request->getView()->renderToResponse(
|
||||
$response,
|
||||
'system/form_page',
|
||||
[
|
||||
'form' => $this->form,
|
||||
'render_mode' => 'edit',
|
||||
'title' => $id ? __('Edit Permission') : __('Add Permission'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function deleteAction(
|
||||
ServerRequest $request,
|
||||
Response $response,
|
||||
int $id,
|
||||
string $csrf
|
||||
): ResponseInterface {
|
||||
$this->doDelete($request, $id, $csrf);
|
||||
|
||||
$request->getFlash()->addMessage('<b>' . __('Permission deleted.') . '</b>', Flash::SUCCESS);
|
||||
return $response->withRedirect((string)$request->getRouter()->named('admin:permissions:index'));
|
||||
}
|
||||
}
|
|
@ -131,26 +131,20 @@ class RolesController extends AbstractAdminApiCrudController
|
|||
|
||||
protected function doUpdatePermissions(Entity\Role $role, array $newPermissions): void
|
||||
{
|
||||
$perms = $role->getPermissions();
|
||||
|
||||
if ($perms->count() > 0) {
|
||||
foreach ($perms as $existing_perm) {
|
||||
$this->em->remove($existing_perm);
|
||||
}
|
||||
$perms->clear();
|
||||
foreach ($role->getPermissions() as $perm) {
|
||||
$this->em->remove($perm);
|
||||
}
|
||||
|
||||
if (!empty($newPermissions['global'])) {
|
||||
if (isset($newPermissions['global'])) {
|
||||
foreach ($newPermissions['global'] as $perm_name) {
|
||||
if ($this->acl->isValidPermission($perm_name, true)) {
|
||||
$perm_record = new Entity\RolePermission($role, null, $perm_name);
|
||||
$this->em->persist($perm_record);
|
||||
$perms->add($perm_record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($newPermissions['station'])) {
|
||||
if (isset($newPermissions['station'])) {
|
||||
foreach ($newPermissions['station'] as $station_id => $station_perms) {
|
||||
$station = $this->em->find(Entity\Station::class, $station_id);
|
||||
|
||||
|
@ -159,7 +153,6 @@ class RolesController extends AbstractAdminApiCrudController
|
|||
if ($this->acl->isValidPermission($perm_name, false)) {
|
||||
$perm_record = new Entity\RolePermission($role, $station, $perm_name);
|
||||
$this->em->persist($perm_record);
|
||||
$perms->add($perm_record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Entity\Repository;
|
||||
|
||||
use App\Acl;
|
||||
use App\Doctrine\Repository;
|
||||
use App\Entity;
|
||||
|
||||
|
@ -59,43 +60,30 @@ class RolePermissionRepository extends Repository
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entity\Role $role
|
||||
* @param array $post_values
|
||||
*/
|
||||
public function setActionsForRole(Entity\Role $role, array $post_values): void
|
||||
public function ensureSuperAdministratorRole(): Entity\Role
|
||||
{
|
||||
$this->em->createQuery(
|
||||
$superAdminRole = $this->em->createQuery(
|
||||
<<<'DQL'
|
||||
DELETE FROM App\Entity\RolePermission rp
|
||||
WHERE rp.role_id = :role_id
|
||||
SELECT r FROM
|
||||
App\Entity\Role r LEFT JOIN r.permissions rp
|
||||
WHERE rp.station IS NULL AND rp.action_name = :action
|
||||
DQL
|
||||
)->setParameter('role_id', $role->getId())
|
||||
->execute();
|
||||
)->setParameter('action', Acl::GLOBAL_ALL)
|
||||
->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
|
||||
foreach ($post_values as $post_key => $post_value) {
|
||||
if (str_contains($post_key, '_')) {
|
||||
[$post_key_action, $post_key_id] = explode('_', $post_key);
|
||||
} else {
|
||||
$post_key_action = $post_key;
|
||||
$post_key_id = null;
|
||||
}
|
||||
|
||||
if ($post_key_action !== 'actions' || empty($post_value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ((array)$post_value as $action_name) {
|
||||
$station = ($post_key_id !== 'global') ? $this->em->getReference(
|
||||
Entity\Station::class,
|
||||
$post_key_id
|
||||
) : null;
|
||||
|
||||
$record = new Entity\RolePermission($role, $station, $action_name);
|
||||
$this->em->persist($record);
|
||||
}
|
||||
if ($superAdminRole instanceof Entity\Role) {
|
||||
return $superAdminRole;
|
||||
}
|
||||
|
||||
$newRole = new Entity\Role();
|
||||
$newRole->setName('Super Administrator');
|
||||
$this->em->persist($newRole);
|
||||
|
||||
$newPerm = new Entity\RolePermission($newRole, null, Acl::GLOBAL_ALL);
|
||||
$this->em->persist($newPerm);
|
||||
|
||||
$this->em->flush();
|
||||
return $newRole;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,6 @@ class Role implements JsonSerializable, Stringable, IdentifiableEntityInterface
|
|||
use Traits\HasAutoIncrementId;
|
||||
use Traits\TruncateStrings;
|
||||
|
||||
public const SUPER_ADMINISTRATOR_ROLE_ID = 1;
|
||||
|
||||
/** @OA\Property(example="Super Administrator") */
|
||||
#[ORM\Column(length: 100)]
|
||||
#[Assert\NotBlank]
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Acl;
|
||||
use App\Config;
|
||||
use App\Entity;
|
||||
use App\Http\ServerRequest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* @extends EntityForm<Entity\Role>
|
||||
*/
|
||||
class PermissionsForm extends EntityForm
|
||||
{
|
||||
protected bool $set_permissions = true;
|
||||
|
||||
public function __construct(
|
||||
protected Entity\Repository\RolePermissionRepository $permissions_repo,
|
||||
EntityManagerInterface $em,
|
||||
Serializer $serializer,
|
||||
ValidatorInterface $validator,
|
||||
Config $config,
|
||||
Entity\Repository\StationRepository $stations_repo,
|
||||
Acl $acl
|
||||
) {
|
||||
$form_config = $config->get('forms/role', [
|
||||
'all_stations' => $stations_repo->fetchArray(),
|
||||
'actions' => $acl->listPermissions(),
|
||||
]);
|
||||
|
||||
parent::__construct($em, $serializer, $validator, $form_config);
|
||||
|
||||
$this->entityClass = Entity\Role::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function process(ServerRequest $request, $record = null): object|bool
|
||||
{
|
||||
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']) && str_contains($attrs['class'], 'permission-select')) {
|
||||
unset($this->fields[$field_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::process($request, $record);
|
||||
}
|
||||
|
||||
protected function denormalizeToRecord(array $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(object $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;
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
<?php $this->layout('main', ['title' => __('Permissions'), 'manual' => true]); ?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary-dark">
|
||||
<h2 class="card-title"><?=__('Permissions')?></h2>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a class="btn btn-outline-primary" role="button" href="<?=$router->named('admin:permissions:add')?>">
|
||||
<i class="material-icons" aria-hidden="true">add</i>
|
||||
<?=__('Add Permission')?>
|
||||
</a>
|
||||
</div>
|
||||
<table class="table table-responsive-md table-striped mb-0">
|
||||
<colgroup>
|
||||
<col width="20%">
|
||||
<col width="30%">
|
||||
<col width="50%">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?=__('Actions')?></th>
|
||||
<th><?=__('Role Name')?></th>
|
||||
<th><?=__('Permissions')?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($roles as $role): ?>
|
||||
<tr class="align-middle">
|
||||
<td class="center">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<a class="btn btn-sm btn-primary" href="<?=$router->named('admin:permissions:edit',
|
||||
['id' => $role['id']])?>"><?=__('Edit')?></a>
|
||||
|
||||
<?php if ($role['id'] != 1): ?>
|
||||
<a class="btn btn-sm btn-danger"
|
||||
data-confirm-title="<?=$this->e(__('Delete role "%s"?', $role['name']))?>"
|
||||
href="<?=$router->named('admin:permissions:delete',
|
||||
['id' => $role['id'], 'csrf' => $csrf])?>"><?=__('Delete')?></a>
|
||||
<?php else: ?>
|
||||
<a class="btn btn-sm btn-danger disabled" href="#"
|
||||
onclick="alert('<?=__('This role cannot be deleted.')?>'); return false;"><?=__('Delete')?></a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="typography-subheading"><?=$this->e($role['name'])?></div>
|
||||
</td>
|
||||
<td>
|
||||
<?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; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
|
@ -41,35 +41,6 @@ class Admin_RecordsCest extends CestAbstract
|
|||
$I->dontSee('test@azuracast.com');
|
||||
}
|
||||
|
||||
/**
|
||||
* @before setupComplete
|
||||
* @before login
|
||||
*/
|
||||
public function manageRoles(FunctionalTester $I): void
|
||||
{
|
||||
$I->wantTo('Manage roles.');
|
||||
|
||||
// Permissions homepage
|
||||
$I->amOnPage('/admin/permissions');
|
||||
$I->see('Super Administrator');
|
||||
|
||||
// Add another role
|
||||
$I->click('Add Permission', '#content');
|
||||
|
||||
$I->submitForm('.form', [
|
||||
'name' => 'ZZZ Test Administrator',
|
||||
]);
|
||||
|
||||
$I->seeCurrentUrlEquals('/admin/permissions');
|
||||
$I->see('ZZZ Test Administrator');
|
||||
|
||||
// Delete the new role
|
||||
$I->click(\Codeception\Util\Locator::lastElement('.btn-danger'));
|
||||
|
||||
$I->seeCurrentUrlEquals('/admin/permissions');
|
||||
$I->dontSee('ZZZ Test Administrator');
|
||||
}
|
||||
|
||||
/**
|
||||
* @before setupComplete
|
||||
* @before login
|
||||
|
|
Loading…
Reference in New Issue