Important
You are browsing the documentation for version 3.1 of OroCommerce, OroCRM and OroPlatform, which is no longer maintained. Read version 5.1 (the latest LTS version) of the Oro documentation to get up-to-date information.
See our Release Process documentation for more information on the currently supported and upcoming releases.
Introduction to Security in Oro Applications¶
The OroSecurityBundle sits on top of the Symfony security layer to reach protect your resources. This means that each user of your application is granted access to a particular subset of your company’s resources. Coincidentally, they have to be prevented from accessing resources, access was not granted to them.
Access Control Lists¶
Access Control Lists are an essential part of the Symfony Security Components. They are leveraged by the OroSecurityBundle to fulfill the requirements of companies in the business context.
Hint
You can find detailed information about Symfony ACL based security model in the Symfony documentation:
Access Levels¶
Access can be granted to a user for a certain resource on several levels. The lowest level is the User level. Being on this level means that users can only access resources that have been assigned to them. At the other end of the hierarchy is the Global level. Users at this level have the permission to access all records within the whole system without exception. The security bundle comes with the following five levels (ordered up from the bottom of the hierarchy):
Level |
Constant |
Description |
---|---|---|
User |
|
The user is granted access to their own records. |
Business Unit |
|
The user is given access to the records in records in all business units they are assign to. |
Division |
|
This is the same as the Business Unit level except that the user can also access all resources that are owned by subordinate units of the business units they are assigned to. |
Organization |
|
The user is given access to all records within the organization, regardless of the business unit the object belongs to or the user is assigned to. |
Global |
|
The user can access all objects within the system. Note This level is available only in Enterprise editions. Global access level makes sense if the user works in the scope of a global organization. If the user works in the scope of an ordinary organization, global level equals organization. |
Each record is associated with an owning organization. When a user logs into the system, they work in the scope of one of their organizations. If a user is a member in several organizations, they can switch the organization scope that is used to perform access checks.
Every record of a security protected entity with ownership type User, Business unit and Organization has an organization.
Note
For all access levels, there is a class constant defined in the Oro\Bundle\SecurityBundle\Acl\AccessLevel class. Its value is shown in the Constant column.
There are two special constants AccessLevel::UNKNOWN
(unknown access level, should not be
assigned to a user) and AccessLevel::NONE_LEVEL
(globally deny access for the user).
Permissions¶
A user can be assigned different modes to a resource. These modes describe what they are allowed to do with the resource. Namely, the following permissions are supported for entities:
Permission |
|
---|---|
|
Whether or not a user is allowed to view a record. |
|
Whether or not a user is allowed to create a record. The access level set for this permission limits the number of owners that can be assigned to a created record. |
|
Whether or not a user is allowed to modify a record. |
|
Whether or not a user is allowed to delete a record. |
|
Whether or not a user is allowed to assign a record to another user. This permission is only evaluated when an entity is edited. |
|
Enterprise Edition Whether or not a user is allowed to assign a record to another user. Only works on entities’ view pages. |
See also
Read the official documentation for a first insight in the usage of ACLs and more complex Access Control List examples.
The following permissions are supported for fields:
Permission |
|
---|---|
|
Whether or not a user is allowed to view a field. |
|
Whether or not a user is allowed to modify a field. |
Configuring Permissions for Entities¶
To be able to protect access to your entities, you first have to configure which permissions
can be granted for a user to them. Use the security
scope in the defaultValues
section of
the @Config
annotation:
1// src/Acme/DemoBundle/Entity/Product.php
2namespace Acme\DemoBundle\Entity;
3
4use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\Config;
5
6/**
7 * @Config(
8 * defaultValues={
9 * ...
10 * "security"={
11 * "type"="ACL",
12 * "permissions"="All",
13 * "group_name"="DemoGroup"
14 * }
15 * )
16 */
17class Product
18{
19 // ...
20}
By default (or when using the special ALL
value for the permissions
property as in the
example above), any available permission can be granted to a user on an
entity. If you want to restrict the available permissions for an entity, you can list them
separated explicitly. For example, you limit it to the VIEW
and EDIT
permissions:
1/**
2 * ...
3 * "security"={
4 * "type"="ACL",
5 * "permissions"="VIEW;EDIT",
6 * "group_name"="DemoGroup"
7 * }
8 * ...
9 */
Protecting Resources¶
After having configured which permissions a user can be granted to a particular entity, you have to make sure that the permissions are taken into account when checking if a user has access to a resource. Depending on the resource, this check can be performed automatically by the OroSecurityBundle or require some additional configuration made by you.
Restricting Access to Controller Methods¶
Let us assume that you have configured an entity to be protectable via ACLs. You have granted some of its objects to a set of users. Now you can control who can enter certain resources through the controller method. Restricting access can be done in two different ways:
Use the
@Acl
annotation on a controller method, providing the entity class name and the permission to check for:1// src/Acme/DemoBundle/Controller/ProductController.php 2namespace Acme\DemoBundle\Controller; 3 4use Oro\Bundle\SecurityBundle\Annotation\Acl; 5use Symfony\Bundle\FrameworkBundle\Controller\Controller; 6 7class ProductController extends Controller 8{ 9 /** 10 * @Acl( 11 * id="product_edit", 12 * type="entity", 13 * class="AcmeDemoBundle:Product", 14 * permission="EDIT" 15 * ) 16 */ 17 public function editAction() 18 { 19 // ... 20 } 21}
When you need to perform a particular check repeatedly, writing
@Acl
over and over again becomes a tedious task. This becomes even a more serious issue when your requirements change and you have to change a lot of ACLs. Luckily, you can configure an ACL globally in your bundle configuration and refer to using the ACL id using the@AclAncestor
annotation.The ACL configuration from the example above looks like this:
1# src/Acme/DemoBundle/Resources/config/oro/acls.yml 2acls: 3 product_edit: 4 type: entity 5 class: AcmeDemoBundle:Product 6 permission: EDIT
The annotation of your controller method becomes a lot smaller then:
1 // src/Acme/DemoBundle/Controller/ProductController.php 2 namespace Acme\DemoBundle\Controller; 3 4 use Oro\Bundle\SecurityBundle\Annotation\AclAncestor; 5 use Symfony\Bundle\FrameworkBundle\Controller\Controller; 6 7 class ProductController extends Controller 8 { 9 /** 10 * @AclAncestor("product_edit") 11 */ 12 public function editAction() 13 { 14 // ... 15 } 16 }
Sometimes you want to protect a controller method coming from code that you do not control. Therefore, you cannot add the
@AclAncestor
annotation to it. Use thebindings
key in the YAML configuration of your ACL to define which method(s) should be protected:1# src/Acme/DemoBundle/Resources/config/oro/acls.yml 2acls: 3 product_edit: 4 type: entity 5 class: AcmeDemoBundle:Product 6 permission: EDIT 7 bindings: 8 - class: Acme\DemoBundle\Controller\ProductController 9 method: editAction
See also
You can read detailed explanations for all available YAML configuration options in the reference section.
Using Param Converters¶
When the @Acl
annotation is used without a param converter, the user’s permission is checked
on the class level. This means that the user is granted access as long as their access level is
not NONE
.
When using the @ParamConverter annotation from the SensioFrameworkExtraBundle together with the
@Acl
annotation, the routing parameters are first converted into the corresponding Doctrine
entity object. Then, access will be checked based on the queried object.
See also
It is also possible to protect Doctrine queries.
Data Grids¶
Records that are part of a data grid are automatically protected by the OroSecurityBundle. View permissions are attached to each record of the data grid.
Protecting Custom DQL Queries¶
When building custom DQL queries, you may want to reduce the result set being returned to the set of domain objects the user is granted access to. To achieve this, use the ACL helper provided by the OroSecurityBundle:
1 // src/Acme/DemoBundle/Controller/DemoController.php
2 namespace Acme\DemoBundle\Controller;
3
4 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
5
6 class DemoController extends Controller
7 {
8 public function protectedAction()
9 {
10 $repository = $this->getDoctrine()->getRepository('AcmeDemoBundle:Product');
11 $queryBuilder = $repository
12 ->createQueryBuilder('p');
13 ->where('p.price > :price')
14 ->orderBy('p.price', 'ASC')
15 ->setParameter('price', 19.99);
16 $aclHelper = $this->get('oro_security.acl_helper');
17 $query = $aclHelper->apply($queryBuilder, 'VIEW');
18
19 // ...
20 }
21 }
In this example, first, a query is built that selects all products from the database which are more
expensive than 19.99
order by their price. Then, the query builder is passed to the apply()
method of the oro_security.acl_helper
service. This service, an instance of the
Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper
class, modifies the query only to the
return entities the user has access to.
Manual Access Checks¶
If you need to manually check the access of the current user to a certain object, you can use the
isGranted()
method from the security.authorization_checker
service for this:
1 // src/Acme/DemoBundle/Controller/DemoController.php
2 namespace Acme\DemoBundle\Controller;
3
4 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
5 use Symfony\Component\Security\Core\Exception\AccessDeniedException;
6
7 class DemoController extends Controller
8 {
9 public function protectedAction()
10 {
11 $entity = ...;
12
13 if (!$this->isGranted('VIEW', $entity)) {
14 throw new AccessDeniedException();
15 }
16
17 // ...
18 }
19 }