# 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();
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://senens-organization.gitbook.io/senenhermida.docs/symfony/security.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
