--- name: oro-security description: "Use when configuring OroCommerce v6.1 ACL, permissions, and access control — setting up entity permissions (acls.yml), ownership types, Acl/AclAncestor PHP attributes on controllers, custom permissions, field-level ACL, access rules for query filtering, or debugging permission issues. Relevant when the user mentions 'ACL', 'permissions', 'access control', 'ownership', 'security', 'acls.yml', 'field ACL', 'access rules', or any OroCommerce authorization task." --- # OroCommerce v6.1 Security & ACL Configuration ## Entity ACL Configuration Place in `Resources/config/oro/acls.yml`: ```yaml acls: acme_demo_document_view: type: entity class: Acme\Bundle\DemoBundle\Entity\Document permission: VIEW description: View documents acme_demo_document_edit: type: entity class: Acme\Bundle\DemoBundle\Entity\Document permission: EDIT ``` Key structure: `type: entity`, `class` (FQCN), `permission` (VIEW, CREATE, EDIT, DELETE, ASSIGN, EXECUTE). ## PHP 8 Acl Attribute Declare ACL requirements directly on controller methods: ```php use Oro\Bundle\SecurityBundle\Attribute\Acl; class DocumentController { #[Acl( id: 'acme_demo_document_view', type: 'entity', class: 'Acme\Bundle\DemoBundle\Entity\Document', permission: 'VIEW' )] public function viewAction(Document $document) { // ACL is checked before this method executes } } ``` Attributes are preferred over YAML bindings — they're colocated with the code they protect and type-safe. ## Ownership and Organization Ownership determines who can access an entity. Four scopes: USER, BUSINESS_UNIT, ORGANIZATION, GLOBAL. **CRITICAL**: If ownership is USER or BUSINESS_UNIT, the entity MUST have an `organization` field. Missing it causes permission checks to fail silently. Define in `Resources/config/oro/entity.yml`: ```yaml entities: Acme\Bundle\DemoBundle\Entity\Document: ownership: owner_type: USER owner_field_name: owner organization_field_name: organization ``` ## AclHelper: Query Filtering ACL checks do NOT happen automatically on queries. You must explicitly apply filtering: ```php class DocumentRepository { public function findApprovedDocuments(User $user) { $qb = $this->createQueryBuilder('d') ->where('d.status = :status') ->setParameter('status', 'approved'); $this->aclHelper->apply($qb); return $qb->getQuery()->getResult(); } } ``` Without `$aclHelper->apply($qb)`, the query returns all entities regardless of permissions. For custom access rules (`AccessRuleInterface`), see [security-patterns.md](references/security-patterns.md). ## Key Pitfalls 1. **Query filtering is not automatic**: Must call `$aclHelper->apply($qb)` explicitly. Common mistake: fetching all entities and filtering in PHP rather than at DB level. 2. **AclAncestor skips object-level checks**: Use `AclAncestor` only for class-level permission verification (list pages). For specific objects, use full `Acl` attributes or `isGranted()`. 3. **Organization field is mandatory for USER/BUSINESS_UNIT ownership**: Omitting it causes permission checks to return false unexpectedly. For `AclAncestor` details, custom permissions, field-level ACL, common patterns, debugging, and additional pitfalls, see [security-patterns.md](references/security-patterns.md). ## Version Notes See [permission-matrix.md](references/permission-matrix.md) for the full permission/ownership matrix and [v6.1 notes](references/v6.1.md).