> For the complete documentation index, see [llms.txt](https://senens-organization.gitbook.io/senenhermida.docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://senens-organization.gitbook.io/senenhermida.docs/symfony/security.md).

# Security

## Security

## Security - User provider, firewall, access control, voters

####

* **Authentication**:
  * Handled by **firewalls** and **user providers**.
  * Firewalls manage how authentication is performed and define security contexts.
  * User providers load user data based on credentials.
* **Authorization**:
  * Managed by **access control rules**, **roles**, and **voters**.
  * Access control rules define which roles can access specific routes.
  * Roles represent user permissions and are assigned to users.
  * Voters implement custom access control logic for fine-grained permissions.

#### Users

create user entity

* loading the user: the user provider.
* Used

  * when login
  * At the beginning of each request, the user is loaded from the session

  ```yaml
  # config/packages/security.yaml
  security:
      # ...

      providers:
          app_user_provider:
              entity:
                  class: App\Entity\User
                  property: email
  ```
* built in providers: entity user provider, ldap user provider, memory user provider, chain user provider (merge of providers), custom provider
* password hashing

  ```yaml
  # config/packages/security.yaml
  security:
      # ...
      password_hashers:
          # Use native password hasher, which auto-selects and migrates the best
          # possible hashing algorithm (which currently is "bcrypt")
          Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
  ```
* FosUser bundle??

#### Firewall

Firewall: Authentication system

The firewall defines which parts of your application are secured and *how* your users will be able to authenticate

```yaml
# config/packages/security.yaml
security:
    # ...
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            # provider that you set earlier inside providers
            provider: app_user_provider

            # activate different ways to authenticate
            # https://symfony.com/doc/current/security.html#firewalls-authentication

            # https://symfony.com/doc/current/security/impersonating_user.html
            # switch_user: true
```

The **`dev`** firewall is really a **fake firewall**: it makes sure that you don't accidentally block Symfony's dev tools - which live under URLs like `/_profiler` and `/_wdt`.

All ***real*****&#x20;URLs** are handled by the **`main`** firewall (no `pattern` key means it matches *all* URLs).

A firewall can have many modes of authentication

* Authenticating Users

From login, json login, http basic, access tokens, login link

#### Authorization

Roles

```yaml
# config/packages/security.yaml
security:
    # ...

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
```

```php
$hasAccess = $this->isGranted('ROLE_ADMIN');
$this->denyAccessUnlessGranted('ROLE_ADMIN')
```

There are 3 ways to deny access: access control, php code, attributes

access\_control in security.yaml

```php
# config/packages/security.yaml
security:
    # ...

    access_control:
				# allow unauthenticated users to access the login form
        - { path: ^/admin/login, roles: PUBLIC_ACCESS }

        # matches /admin/users/*
        - { path: '^/admin/users', roles: ROLE_SUPER_ADMIN }

        # matches /admin/* except for anything matching the above rule
        - { path: '^/admin', roles: ROLE_ADMIN }
```

php code

```php
// src/Controller/AdminController.php
// ...

public function adminDashboard(): Response
{
    $this->denyAccessUnlessGranted('ROLE_ADMIN');

    // or add an optional message - seen by developers
    $this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'User tried to access a page without having ROLE_ADMIN');
}
```

```php
// src/Controller/AdminController.php
// ...

use Symfony\Component\Security\Http\Attribute\IsGranted;

#[IsGranted('ROLE_ADMIN')]
class AdminController extends AbstractController
{
    // Optionally, you can set a custom message that will be displayed to the user
    #[IsGranted('ROLE_SUPER_ADMIN', message: 'You are not allowed to access the admin dashboard.')]
    public function adminDashboard(): Response
    {
        // ...
    }
}
```

You can deny access to

* Access control
  * match access control entry
  * enforce access restrictions
    * roles: If the user does not have the given role, then access is denied
    * allow\_if: If the expression returns false, then access is denied;
    * requires\_channel: If the incoming request's channel (e.g. http) does not match this value (e.g. https), the user will be redirected

**Checking to see if a User is Logged In**

If you *only* want to check if a user is logged in (you don't care about roles), you have the following two options.

Firstly, if you've given *every* user `ROLE_USER`, you can check for that role.

Secondly, you can use the special "attribute" `IS_AUTHENTICATED_FULLY` in place of a role:

**Voters**

Voters in Symfony are components used to implement complex authorization logic beyond simple role checks.

1. **Affirmative**: This strategy grants access as soon as any voter returns a positive response. If all voters abstain, access is denied.
2. **Consensus**: Access is granted if there are more grants than denies. Abstentions are not counted. If the number of grant and deny votes is equal, the decision is based on the **`allowIfEqualGrantedDenied`** configuration (grants access if true, denies otherwise).
3. **Unanimous**: This requires all voters to grant access. If any voter denies access, or if all abstain, access is denied.

```php
public function edit(Post $post, AuthorizationCheckerInterface $authorizationChecker)
{
    // check for "edit" access: calls all voters
    $this->denyAccessUnlessGranted('edit', $post);

    // ... perform some action, such as saving the post to the database

    return new Response('Post edited successfully');
}
```

```php
// src/Security/PostVoter.php
namespace App\Security;

class PostVoter extends Voter
{
    const EDIT = 'edit';

    protected function supports($attribute, $subject)
    {
        // if the attribute isn't one we support, return false
        if (!in_array($attribute, [self::EDIT])) {
            return false;
        }

        // only vote on `Post` objects
        if (!$subject instanceof Post) {
            return false;
        }

        return true;
    }

    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        $user = $token->getUser();

        if (!$user instanceof UserInterface) {
            // the user must be logged in; if not, deny access
            return false;
        }

        // you know $subject is a Post object, thanks to the supports() method
        /** @var Post $post */
        $post = $subject;

        switch ($attribute) {
            case self::EDIT:
                return $this->canEdit($post, $user);
        }

        throw new \LogicException('This code should not be reached!');
    }

    private function canEdit(Post $post, UserInterface $user)
    {
        // if they are the owner of the post, grant permission
        return $user === $post->getOwner();
    }
}
```
