It seems like a basic need but can't find any resource on the subject.
In the User Admin form, I need to filter the selectable groups depending on current user's roles.
Let's say we should only be able to select groups for which we are granted to all attached roles.
I think ACLs are not required here.
Could I use a voter ?
As it seems to me like a common need, could someone point me to any resource ?
As I can see the maddening rush to respond to my question, I'm going to give my humble contribution.
I have not been able to use ACL nor voters for that.
So I did a custom type extending choice
This type retrieves the groups from a service with security.context and doctrine injected.
This service have a function for gathering groups that have this little algorithm:
public function allExistingGroups()
{
$finalGroupsList = array();
/* Get all existing groups */
$groups = $this->doctrine->getRepository('SonataUserBundle:Group')
->findAll();
foreach ($groups as $group) {
$isGranted = true;
foreach ($group->getRoles() as $role) {
if (!$this->securityContext->isGranted($role)) {
$isGranted = false;
}
}
/* User is granted, so he can see the group */
if ($isGranted) {
$finalGroupsList[$group->getId()] = $group->getName();
}
}
return $finalGroupsList;
}
The user will now see only groups to which he is granted to all the roles.
For sure there should be a prettier solution, but this did the trick for me.
Related
I'm working with symfony 2.3. How I can show the user roles without (ROLE_). I want to change the view, but in the database are intact.
When I display the roles in the view I have this
ROLE_ADMIN
ROLE_CONSULTOR
and I want
Admin
Consultor
Role Entities just need to implement the RoleInterface. You can add your own custom fields such as the names you want.
http://api.symfony.com/2.3/Symfony/Component/Security/Core/Role/RoleInterface.html
Not clear what you want to achieve, but if there are few roles, may be 2-3, (e.g. ROLE_ADMIN, ROLE_CONSULTOR, ROLE_USER), just define a method which receives the system role, returns the human readable one. May be like this:
public function convertToHumanreadable($role)
{
$return = null;
switch ($role) {
case 'ROLE_ADMIN':
$return = 'Admin';
break;
...
}
return $return;
}
Or even like this:
public function convertToHumanreadable($role)
{
$roleParts = explode('_', $role)
return ucfirst(strtolower($roleParts[1]));
}
Or you can create your own role entity by implementing the RoleInterface as #DerickF mentioned.
It's a question about SecurityVoter.
With Symfony 2.6, you can create simpler security voters to check if user have specifics permissions on a object:
protected function isGranted($attribute, $object, $user = null)
{
switch ($attribute) {
case self::VIEW:
return $object->getAuthor() === $user;
case self::EDIT:
return $object->getAuthor() === $user && time() - $object->getAddedAt()->getTimestamp() <= TicketMessage::PERMIT_EDIT_GAP;
default:
return false;
}
}
That works. But what if I want to check an attributes based on the class instead of a object ? For example, a 'create' permissions for creating a new object that not exists for the moment.
Code sample:
is_granted('create', 'AppBundle\Entity\MyEntity')
or:
is_granted('create', 'AppBundle:Entity')
Is there a way to do that ?
Thanks.
Please, read the documentation on Symfony Best Practicles and Symfony Voters for better understanding the concepts of Symfony.
If you really need to check permissions based only on class attributes, then the decision is going to be constant. So, the recommended way to control your application's logic is described in Symfony Best Practicles:
Best Practice: Use constants to define configuration options that
rarely change.
As you can see, there are two recommended ways of doing what you want:
use constants (For example you can define some constants in your
custom YourBundle\SecurityManager)
use parameters.yml for configuration
I'm building an application where users are tied to accounts (as in, multiple users all share an account), then other entities - lets call them products are tied to the accounts. The products are associated with the accounts and only users that are tied to that account can edit the products.
The difference being in my case, there are many different entities being shared in the same model.
If it was just the one (product) entity, it wouldn't be a problem to have a method in my ProductRepository like:
public function checkOwnership($id, $account)
{
$count = $this->createQueryBuilder('s')
->select('count(s.id)')
->where('s.account = :acc')
->andWhere('s.id = :id')
->setParameters(array('id' => $id, 'acc' => $account))
->getQuery()
->getSingleScalarResult();
if($count == 0)
throw $this->createNotFoundException('ye..');
return;
}
To make sure the id of the product is tied to the user's account.
The account argument is passed to this function by using:
$this->getUser();
In the controller.
What is the best way for me to make this function available to the other entities to prevent code duplication? There are (currently) only 3 other entities, so it wouldn't be the end of the world to just have it in each repository, but I'm just wondering if there were a way to create a 'common' or global repository that follows good standards? I'd sure like to know about it.
Edit:
Or have I just completely over-thought this? Can I just create a 'Common' directory in my 'MainBundle', define a namespace and add a use statement at the start of my controllers that need access to the function?
I hope I fully understand your question.
Solution one, duplication, easy: let #ParamConverter do the job (automatic response to 404 if doesn't exist)
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* #Route("/pets/{id}")
*/
public function showAction(Pet $pet)
{
// Current logged user
$currentUser = $this->get('security.context')->getToken()->getUser();
// Owner
$ownerUser = $pet->getUser();
if(!$currentUser->equals($ownerUser)) {
// 401, 403 o 404 depends on you
}
}
Solution two, DRY, a bit harder: use JMSAOPBundle and define an interceptor that intercepts all request to you controller and actions (show, delete, etc.). The pointcut (connected to the interceptor) will get the current user from the context and the owner from the entity. Then you do the check...
I have a module that creates (and updates) Drupal 7 nodes programmatically.
Since the content of the body these nodes is changed by a program at random intervals I do not want anyone, including the administrator, to be able to edit them. Is there a way way to completely "turn-off" the interface that allows a administrator to edit a node?
If it's a standard user with an administrator role you can implement hook_node_access() in your custom module:
function MYMODULE_node_access($node, $op, $account) {
$type = is_string($node) ? $node : $node->type;
if ($type == 'the_type' && $op == 'update') {
return NODE_ACCESS_DENY;
}
return NODE_ACCESS_IGNORE;
}
If it's the 'super user' (user 1) you need to get a bit more creative as a lot of access checks are bypassed for that user.
You can implement hook_menu_alter() to override the access callback for the node edit page, and provide your own instead:
function MYMODULE_menu_alter(&$items) {
$items['node/%node/edit']['access callback'] = 'MYMODULE_node_edit_form_access';
}
function MYMODULE_node_edit_form_access($node) {
$type = is_string($node) ? $node : $node->type;
if ($type == 'my_type') {
return FALSE;
}
return node_access('update', $node);
}
I like both of Clive's suggestions, but one more option is to simply disable the fields using HOOK_form_alter. This will work for the User 1 account too. I've used this recently to disable a specific field that I don't want anyone modifying.
function YOURMODULE_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'your_form_id') {
$form['body_field']['body']['#disabled'] = TRUE;
$form['body_field']['body']['#value'] = $form['body_field']['body']['#default_value'];
}
}
Admittedly, this solution isn't ideal if you're using the built-in body field because of the teaser. But it works great if you want to disable editing of certain fields while leaving other aspects of the node editable and the page intact.
I'm not sure why you need them to be an administrator. That role, honestly, should be reserved for people with absolute control -- and even those people should not use it as their "main" account due to the potential for destroying things. Why not just make an "editor" role or something similar, and give all the permissions you need?
On my site, I have three roles:
Role 1
Role 2
Role 3
Role 1 may only ever have 10 nodes of type "NODE_TYPE".
Role 2 may only ever have 100 nodes of type "NODE_TYPE".
Role 3 may only ever have 1000 nodes of type "NODE_TYPE".
What can I use to enforce this? The following modules don't do the trick:
node_limit
user quota
Anyone?
How this can be achieved largely depends on how NODE_TYPE is created.
Assuming you have a NODE_TYPE module you could implement hook_validate by doing something like this:
function NODE_TYPE_validate($node, &$form) {
if (NODE_TYPE_reached_post_limit()) {
form_set_error('form_name', t('You have reached your post limit'));
}
}
function NODE_TYPE_reached_post_limit() {
global $user;
//Write code to do the following:
//-> Check which group $user belongs to
//-> Create query to see how many posts $user has made
//-> Return true if $user has reached the limit
}
If you do not have access to the module that creates the NODE_TYPE you could create a new module and implement hook_nodeapi:
function yournewmodule_nodeapi(&$node, $op) {
switch ($op) {
case 'validate':
if ($node->type == "NODE_TYPE" && yournewmodule_reached_post_limit()) {
form_set_error('form_name', t('You have reached your post limit'));
}
break;
}
}
function yournewmodule_reached_post_limit() {
global $user;
//Write code to do the following:
//-> Check which group $user belongs to
//-> Create query to see how many posts $user has made
//-> Return true if $user has reached the limit
}
I'm not 100% sure whether validate is the best hook to implement but it certainly is an option.