senenhermida.docs
  • Design Patterns
  • SOLID
  • PHP History
    • Late static binding
  • PHP Questions
  • Symfony
    • Service container
    • Event dispatcher
    • Security
    • Validation
    • Serializer
    • Http Kernel Component
    • Cache component
    • Symfony history
  • Algorithms
  • Leetcode
    • Codility
  • My CV
Powered by GitBook
On this page
  • Security
  • Security - User provider, firewall, access control, voters
  1. Symfony

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

    # 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

    # 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

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

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

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
$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

# 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

// 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');
}
// 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.

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');
}
// 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();
    }
}
PreviousEvent dispatcherNextValidation