# Oro Security

> 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.

- Skill: `netresearch/oro-security` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add netresearch/oro-security`
- Raw SKILL.md: https://api.skillmd.com/api/skills/netresearch/oro-security/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: netresearch (https://skillmd.com/u/netresearch)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/netresearch/oro-security

---


# 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).

