Last active
August 21, 2018 17:43
-
-
Save emulienfou/002317d8015fea34ef7fd0c49b8e59ac to your computer and use it in GitHub Desktop.
EasyAdmin role based permissions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% for action in actions %} | |
{# Check role based permission for current user #} | |
{% if action.roles is not defined or in_array(action.roles, app.user.roles) %} | |
{% if 'list' == action.name %} | |
{% set action_href = request_parameters.referer|default('') ? request_parameters.referer|easyadmin_urldecode : path('easyadmin', request_parameters|merge({ action: 'list' })) %} | |
{% elseif 'method' == action.type %} | |
{% set action_href = path('easyadmin', request_parameters|merge({ action: action.name, id: item_id })) %} | |
{% elseif 'route' == action.type %} | |
{% set action_href = path(action.name, request_parameters|merge({ action: action.name, id: item_id })) %} | |
{% endif %} | |
<a class="{{ action.css_class|default('') }}" title="{{ action.title|default('') is empty ? '' : action.title|trans(trans_parameters, translation_domain) }}" href="{{ action_href }}" target="{{ action.target }}"> | |
{%- if action.icon %}<i class="fa fa-{{ action.icon }}"></i> {% endif -%} | |
{%- if action.label is defined and not action.label is empty -%} | |
{{ action.label|trans(arguments = trans_parameters|merge({ '%entity_id%': item_id }), domain = translation_domain) }} | |
{%- endif -%} | |
</a> | |
{% endif %} | |
{% endfor %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme\Controller; | |
use EasyCorp\Bundle\EasyAdminBundle\Controller\AdminController as BaseAdminController; | |
use EasyCorp\Bundle\EasyAdminBundle\Exception\ForbiddenActionException; | |
use Symfony\Component\HttpFoundation\{RedirectResponse, Request, Response}; | |
use Symfony\Component\Routing\Annotation\Route; | |
/** | |
* Class AdminController | |
* | |
* @package Acme\Controller | |
*/ | |
class AdminController extends BaseAdminController | |
{ | |
/** | |
* @Route("/", name="easyadmin") | |
* @Route("/", name="admin") | |
* The 'admin' route is deprecated since version 1.8.0 and it will be removed in 2.0. | |
* | |
* @param Request $request | |
* | |
* @return RedirectResponse|Response | |
*/ | |
public function indexAction(Request $request) | |
{ | |
if ('admin' === $request->attributes->get('_route')) { | |
trigger_error(sprintf('The "admin" route is deprecated since version 1.8.0 and it will be removed in 2.0. Use the "easyadmin" route instead.'), | |
E_USER_DEPRECATED); | |
} | |
$this->initialize($request); | |
if (null === $request->query->get('entity')) { | |
if (in_array('ROLE_TEACHER', $this->getUser()->getRoles())) { | |
return $this->redirect($this->get('router')->generate('easyadmin', [ | |
'action' => 'list', | |
'entity' => 'Availability' | |
])); | |
} | |
return $this->redirectToBackendHomepage(); | |
} | |
$action = $request->query->get('action', 'list'); | |
if (!$this->isActionAllowed($action)) { | |
throw new ForbiddenActionException(['action' => $action, 'entity_name' => $this->entity['name']]); | |
} | |
$this->checkPermissions(); | |
return $this->executeDynamicMethod($action . '<EntityName>Action'); | |
} | |
/** | |
* Check roles based permissions | |
* | |
* @see: https://github.com/EasyCorp/EasyAdminBundle/issues/807 | |
*/ | |
protected function checkPermissions() | |
{ | |
if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) { | |
throw $this->createAccessDeniedException(); | |
} | |
$easyAdmin = $this->request->attributes->get('easyadmin'); | |
$action = $this->request->query->get('action'); | |
$perms = []; | |
// Roles by entity | |
if (isset($easyAdmin['entity']['roles'])) { | |
$perms = $easyAdmin['entity']['roles']; | |
} | |
// Roles by entity's action | |
if (isset($easyAdmin['entity'][$action]['roles'])) { | |
$perms = $easyAdmin['entity'][$action]['roles']; | |
} | |
$this->denyAccessUnlessGranted($perms); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
easy_admin: | |
design: | |
menu: | |
- { label: 'Home', route: 'index', icon: 'home' } | |
- { entity: 'User', label: 'Users', icon: 'users', roles: ['ROLE_ADMIN', 'ROLE_SUPER_ADMIN'] } | |
- { entity: 'Student', label: 'Student', icon: 'graduation-cap', roles: 'ROLE_TEACHER' } | |
entities: | |
User: | |
class: Acme\Entity\User | |
roles: ['ROLE_ADMIN', 'ROLE_SUPER_ADMIN'] | |
Student: | |
class: Acme\Entity\Student | |
roles: ['ROLE_TEACHER', 'ROLE_ADMIN', 'ROLE_SUPER_ADMIN'] | |
actions: | |
- 'edit' | |
- { name: 'delete', roles: ['ROLE_ADMIN', 'ROLE_SUPER_ADMIN'] } | |
fields: | |
- { property: 'student', label: 'Student' } | |
- { property: 'user', label: 'User', roles: ['ROLE_ADMIN', 'ROLE_SUPER_ADMIN'] } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% set _entity_config = easyadmin_entity(app.request.query.get('entity')) %} | |
{% trans_default_domain _entity_config.translation_domain %} | |
{% set _trans_parameters = { '%entity_name%': _entity_config.name|trans, '%entity_label%': _entity_config.label|trans } %} | |
{% extends _entity_config.templates.layout %} | |
{% set _request_parameters = app.request.query.all|merge(_request_parameters|default({}))|merge({ | |
action: app.request.get('action'), | |
entity: _entity_config.name, | |
menuIndex: app.request.get('menuIndex'), | |
submenuIndex: app.request.get('submenuIndex'), | |
sortField: app.request.get('sortField', ''), | |
sortDirection: app.request.get('sortDirection', 'DESC'), | |
page: app.request.get('page', 1), | |
referer: null | |
}) %} | |
{% if 'search' == app.request.get('action') %} | |
{% set _request_parameters = _request_parameters|merge({ | |
query: app.request.get('query')|default(''), | |
sortField: _entity_config.search.sort.field|default(app.request.get('sortField', '')), | |
sortDirection: _entity_config.search.sort.direction|default(app.request.get('sortDirection', 'DESC')), | |
}) %} | |
{% endif %} | |
{% set _request_parameters = _request_parameters|merge({ referer: path('easyadmin', _request_parameters)|url_encode }) %} | |
{% block body_id 'easyadmin-list-' ~ _entity_config.name %} | |
{% block body_class 'list list-' ~ _entity_config.name|lower %} | |
{% block content_title %} | |
{% spaceless %} | |
{% if 'search' == app.request.get('action') %} | |
{% set _default_title = 'search.page_title'|transchoice(paginator.nbResults, {}, 'EasyAdminBundle') %} | |
{{ (_entity_config.search.title is defined ? _entity_config.search.title|transchoice(paginator.nbResults) : _default_title)|raw }} | |
{% else %} | |
{% set _default_title = 'list.page_title'|trans(_trans_parameters, 'EasyAdminBundle') %} | |
{{ (_entity_config.list.title is defined ? _entity_config.list.title|trans(_trans_parameters) : _default_title)|raw }} | |
{% endif %} | |
{% endspaceless %} | |
{% endblock %} | |
{% block content_header %} | |
<div class="row"> | |
<div class="col-sm-5"> | |
{% block content_title_wrapper %} | |
<h1 class="title">{{ block('content_title') }}</h1> | |
{% endblock %} | |
</div> | |
<div class="col-sm-7"> | |
<div class="global-actions"> | |
{% block global_actions %} | |
{% if easyadmin_action_is_enabled_for_list_view('search', _entity_config.name) %} | |
{% set _action = easyadmin_get_action_for_list_view('search', _entity_config.name) %} | |
{% block search_action %} | |
<div class="form-action {{ _action.css_class|default('') }}"> | |
<form method="get" action="{{ path('easyadmin') }}"> | |
{% block search_form %} | |
<input type="hidden" name="action" value="search"> | |
<input type="hidden" name="entity" value="{{ _request_parameters.entity }}"> | |
<input type="hidden" name="sortField" value="{{ _entity_config.search.sort.field|default(_request_parameters.sortField) }}"> | |
<input type="hidden" name="sortDirection" value="{{ _entity_config.search.sort.direction|default(_request_parameters.sortDirection) }}"> | |
<input type="hidden" name="menuIndex" value="{{ _request_parameters.menuIndex }}"> | |
<input type="hidden" name="submenuIndex" value="{{ _request_parameters.submenuIndex }}"> | |
<div class="input-group"> | |
<input class="form-control" type="search" name="query" value="{{ app.request.get('query')|default('') }}"> | |
<span class="input-group-btn"> | |
<button class="btn" type="submit" formtarget="{{ _action.target }}"> | |
<i class="fa fa-search"></i> | |
<span class="hidden-xs hidden-sm">{{ _action.label|default('action.search')|trans(_trans_parameters) }}</span> | |
</button> | |
</span> | |
</div> | |
{% endblock %} | |
</form> | |
</div> | |
{% endblock search_action %} | |
{% endif %} | |
{% if easyadmin_action_is_enabled_for_list_view('new', _entity_config.name) %} | |
{% set _action = easyadmin_get_action_for_list_view('new', _entity_config.name) %} | |
{% block new_action %} | |
<div class="button-action"> | |
<a class="{{ _action.css_class|default('') }}" href="{{ path('easyadmin', _request_parameters|merge({ action: _action.name })) }}" target="{{ _action.target }}"> | |
{% if _action.icon %}<i class="fa fa-{{ _action.icon }}"></i>{% endif %} | |
{{ _action.label is defined and not _action.label is empty ? _action.label|trans(_trans_parameters) }} | |
</a> | |
</div> | |
{% endblock new_action %} | |
{% endif %} | |
{% endblock global_actions %} | |
</div> | |
</div> | |
</div> | |
{% endblock content_header %} | |
{% block main %} | |
{% set _list_item_actions = easyadmin_get_actions_for_list_item(_entity_config.name) %} | |
<div class="table-responsive"> | |
<table class="table"> | |
<thead> | |
{% block table_head %} | |
<tr> | |
{% for field, metadata in fields %} | |
{% set isSortingField = metadata.property == app.request.get('sortField')|split('.')|first %} | |
{% set nextSortDirection = isSortingField ? (app.request.get('sortDirection') == 'DESC' ? 'ASC' : 'DESC') : 'DESC' %} | |
{% set _column_label = (metadata.label ?: field|humanize)|trans(_trans_parameters) %} | |
{% set _column_icon = isSortingField ? (nextSortDirection == 'DESC' ? 'fa-caret-up' : 'fa-caret-down') : 'fa-sort' %} | |
{# Check role based permission for current user #} | |
{% if metadata.roles is not defined or in_array(metadata.roles, app.user.roles) %} | |
<th data-property-name="{{ metadata.property }}" | |
class="{{ isSortingField ? 'sorted' }} {{ metadata.virtual ? 'virtual' }} {{ metadata.dataType|lower }} {{ metadata.css_class }}" {{ easyadmin_config('design.rtl') ? 'dir="rtl"' }}> | |
{% if metadata.sortable %} | |
<a href="{{ path('easyadmin', _request_parameters|merge({ page: 1, sortField: metadata.property, sortDirection: nextSortDirection })) }}"> | |
<i class="fa {{ _column_icon }}"></i> | |
{{ _column_label|raw }} | |
</a> | |
{% else %} | |
<span>{{ _column_label|raw }}</span> | |
{% endif %} | |
</th> | |
{% endif %} | |
{% endfor %} | |
{% if _list_item_actions|length > 0 %} | |
<th {{ easyadmin_config('design.rtl') ? 'dir="rtl"' }}> | |
<span>{{ 'list.row_actions'|trans(_trans_parameters, 'EasyAdminBundle') }}</span> | |
</th> | |
{% endif %} | |
</tr> | |
{% endblock table_head %} | |
</thead> | |
<tbody> | |
{% block table_body %} | |
{% for item in paginator.currentPageResults %} | |
{# the empty string concatenation is needed when the primary key is an object (e.g. an Uuid object) #} | |
{% set _item_id = '' ~ attribute(item, _entity_config.primary_key_field_name) %} | |
<tr data-id="{{ _item_id }}"> | |
{% for field, metadata in fields %} | |
{% set isSortingField = metadata.property == app.request.get('sortField') %} | |
{% set _column_label = (metadata.label ?: field|humanize)|trans(_trans_parameters) %} | |
{# Check role based permission for current user #} | |
{% if metadata.roles is not defined or in_array(metadata.roles, app.user.roles) %} | |
<td data-label="{{ _column_label }}" | |
class="{{ isSortingField ? 'sorted' }} {{ metadata.dataType|lower }} {{ metadata.css_class }}" {{ easyadmin_config('design.rtl') ? 'dir="rtl"' }}> | |
{{ easyadmin_render_field_for_list_view(_entity_config.name, item, metadata) }} | |
</td> | |
{% endif %} | |
{% endfor %} | |
{% if _list_item_actions|length > 0 %} | |
{% set _column_label = 'list.row_actions'|trans(_trans_parameters, 'EasyAdminBundle') %} | |
<td data-label="{{ _column_label }}" class="actions"> | |
{% block item_actions %} | |
{{ include('@EasyAdmin/default/includes/_actions.html.twig', { | |
actions: _list_item_actions, | |
request_parameters: _request_parameters, | |
translation_domain: _entity_config.translation_domain, | |
trans_parameters: _trans_parameters, | |
item_id: _item_id | |
}, with_context = false) }} | |
{% endblock item_actions %} | |
</td> | |
{% endif %} | |
</tr> | |
{% else %} | |
<tr> | |
<td class="no-results" colspan="{{ _list_item_actions|length > 0 ? fields|length + 1 : fields|length }}"> | |
{{ 'search.no_results'|trans(_trans_parameters, 'EasyAdminBundle') }} | |
</td> | |
</tr> | |
{% endfor %} | |
{% endblock table_body %} | |
</tbody> | |
</table> | |
</div> | |
{% block paginator %} | |
{{ include(_entity_config.templates.paginator) }} | |
{% endblock paginator %} | |
{% block delete_form %} | |
{% set referer = paginator.currentPage == paginator.nbPages and 1 != paginator.currentPage and 1 == paginator.currentPageResults|length | |
? path('easyadmin', app.request.query|merge({ page: app.request.query.get('page') - 1 })) | |
: app.request.requestUri | |
%} | |
{{ include('@EasyAdmin/default/includes/_delete_form.html.twig', { | |
view: 'list', | |
referer: referer|url_encode, | |
delete_form: delete_form_template, | |
_translation_domain: _entity_config.translation_domain, | |
_trans_parameters: _trans_parameters, | |
_entity_config: _entity_config, | |
}, with_context = false) }} | |
{% endblock delete_form %} | |
{% endblock main %} | |
{% block body_javascript %} | |
{{ parent() }} | |
<script type="text/javascript"> | |
$(function() { | |
$('#main').find('table .toggle input[type="checkbox"]').change(function() { | |
var toggle = $(this); | |
var newValue = toggle.prop('checked'); | |
var oldValue = !newValue; | |
var columnIndex = $(this).closest('td').index() + 1; | |
var propertyName = $('table th.toggle:nth-child(' + columnIndex + ')').data('property-name'); | |
var toggleUrl = "{{ path('easyadmin', { action: 'edit', entity: _entity_config.name, view: 'list' })|raw }}" | |
+ "&id=" + $(this).closest('tr').data('id') | |
+ "&property=" + propertyName | |
+ "&newValue=" + newValue.toString(); | |
var toggleRequest = $.ajax({ type: "GET", url: toggleUrl, data: {} }); | |
toggleRequest.done(function(result) {}); | |
toggleRequest.fail(function() { | |
// in case of error, restore the original value and disable the toggle | |
toggle.bootstrapToggle(oldValue == true ? 'on' : 'off'); | |
toggle.bootstrapToggle('disable'); | |
}); | |
}); | |
$('.action-delete').on('click', function(e) { | |
e.preventDefault(); | |
var id = $(this).parents('tr').first().data('id'); | |
$('#modal-delete').modal({ backdrop: true, keyboard: true }) | |
.off('click', '#modal-delete-button') | |
.on('click', '#modal-delete-button', function () { | |
var deleteForm = $('#delete-form'); | |
deleteForm.attr('action', deleteForm.attr('action').replace('__id__', id)); | |
deleteForm.trigger('submit'); | |
}); | |
}); | |
}); | |
</script> | |
{% if 'search' == app.request.get('action') %} | |
<script type="text/javascript"> | |
var _search_query = "{{ app.request.get('query')|default('')|e('js') }}"; | |
// the original query is prepended to allow matching exact phrases in addition to single words | |
$('#main').find('table tbody').highlight($.merge([_search_query], _search_query.split(' '))); | |
</script> | |
{% endif %} | |
{% endblock %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services: | |
# Twig extension | |
Acme\Twig\AcmeExtension: | |
tags: | |
- { name: twig.extension } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme\Twig; | |
use Twig\Extension\AbstractExtension; | |
use Twig\TwigFunction; | |
/** | |
* Class AcmeExtension | |
* | |
* @package Acme\Twig | |
*/ | |
class AcmeExtension extends AbstractExtension | |
{ | |
/** | |
* @return array|\Twig_Function[] | |
*/ | |
public function getFunctions() | |
{ | |
return [ | |
new TwigFunction('in_array', [$this, 'inArray', ['haystack', 'target']]) | |
]; | |
} | |
/** | |
* Check if at least one of $target is in $haystack | |
* | |
* @param array $haystack | |
* @param array $target | |
* | |
* @return bool | |
*/ | |
public function inArray($haystack, array $target): bool | |
{ | |
return count(array_intersect((array)$haystack, $target)) > 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Are you sure that "actions" works? Because "edit", "show" and "delete" are still visible and I still have access