Utilisation d'une expression pour désactiver la sécurisation d'une administration Symfony en environnement de dev

Publié le 15/10/2020 • Actualisé le 15/10/2020

Dans cet article nous allons voir comment utiliser une expression pour désactiver la sécurisation d'une administration Symfony en environnement de développement. Nous n'allons pas utiliser un test basé sur l'IP de la requête comme l'explique la documentation, mais nous allons utiliser l'environnement de l'application. C'est parti ! 😎


English language detected! 🇬🇧

  We noticed that your browser is using English. Do you want to read this post in this language?

Read the english version 🇬🇧 Close

» Publié dans "Une semaine Symfonique 720" (du 12 au 18 octobre 2020).

Prérequis

Je présumerai que vous avez les compétences de base concernant Symfony et de son composant relatif à la sécurité.

Configuration

J'ai déjà migré vers Symfony 5.2, il n'y a pas de différence notable avec la 5.1 pour cet article.

  • PHP 8.3
  • Symfony 6.4.6

Introduction

Sur ce projet et plusieurs "petits" projet, j'utilise le binaire Symfony pour développer et le bundle EasyAdmin afin d'avoir une interface basique d'administration pour mes bases de données. Cette interface et bien sûr sécurisée pour que personne ne puisse y accéder à part moi. Pour ce genre de projets, j'utilise souvent l'authentification http_basic qui est rapide à mettre en place et évite de créer des tables pour les utilisateurs. Quand on accède à l'admin, on a le po-pup navigateur suivant :


La popup d'authentification HTTP basique Firefox

Ce popup du navigateur est pratique à utiliser puisque ça évite la création d'un formulaire de login dans l'application. Il est aussi gênante car, il est modal et bloque le navigateur jusqu'à la confirmation d'un login et mot de passe ou de l'annulation. On ne peut même pas changer d'onglet. C'est pourquoi je voulais l'éviter pendant que je développe, pour améliorer, encore une fois, l'expérience développeur (DX).

Quid de la documentation ?

Tout d'abord, voyons ce dit la documentation ; les exemples montrent qu'on peut utiliser un "test par IP" pour détecter l'environnement local. Dans ce premier exemple, l'accès est autorisé si la requête correspond à une IP ou une plage d'IPs donnée grâce à l'option ips :

# config/packages/security.yaml
security:
    # ...
    access_control:
        #
        # the 'ips' option supports IP addresses and subnet masks
        - { path: '^/internal', roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1, 192.168.0.1/24] }
        - { path: '^/internal', roles: ROLE_NO_ACCESS }

Dans le deuxième exemple, une expression est utilisée. Cette expression vérifie si l'IP de la requête est locale ou si les entêtes HTTP contiennent une clé donnée :

# config/packages/security.yaml
security:
    # ...
    access_control:
        -
            path: ^/_internal/secure
            # the 'role' and 'allow-if' options work like an OR expression, so
            # access is granted if the expression is TRUE or the user has ROLE_ADMIN
            roles: 'ROLE_ADMIN'
            allow_if: "'127.0.0.1' == request.getClientIp() or request.headers.has('X-Secure-Access')"

Utilisation de l'environnement applicatif

Maintenant, voyons une approche différente. Nous voulons que l'administration soit accessible sans identification seulement si nous sommes en environnement de dev. Comme nous l'avons vu précédemment dans la documentation, au sein du fichier security.yml, le composant expression language a accès à l'objet request. On peut accéder à l'environnement actif grâce au panier de paramètres du serveur :

    access_control:
        - { path: '^/admin', roles: ROLE_ADMIN, allow_if: "'dev' == request.server.get('APP_ENV')" } # role OR allow if...

Maintenant ouvrez votre navigateur et accédez à l'URL d'admin. Vous ne devriez plus avoir à vous identifier comme avant. Vous pouvez modifier vos fichiers .env ou .env.local afin de vérifier que le pop-up de login est bien de retour si on bascule en environnement de production. Mieux, ajoutons un test fonctionnel pour cela. Quand on lance les tests, l'environnement "test" est utilisé, cela veut dire qu'on ne pourra pas accéder à l'administration sans s'identifier, l'accès sera sécurisé tout comme en production. Ce qui est bien ici, c'est que le test va marcher en local ou sur la CI et ce, sans avoir à simuler une "fausse" IP. Voici notre test, il s'assure qu'on a un code de retour HTTP non autorisé dès lors qu'on accède directement à l'URL d'admin.

<?php

declare(strict_types=1);

namespace App\Tests\Functional\Controller\Admin;

use App\Tests\WebTestCase;
use Symfony\Component\HttpFoundation\Response;

/**
 * @see DashboardController
 */
final class DashboardControllerTest extends WebTestCase
{
    /**
     * @see DashboardController::index
     */
    public function testIndex(): void
    {
        $client = self::createClient();
        $client->request('GET', '/admin');
        self::assertResponseStatusCodeSame(Response::HTTP_UNAUTHORIZED);
    }
}

Finalement, on peut ajouter un lien pour accéder à l'admin dans le layout. Nous pourrions utiliser le même test (avec le préfixe app) app.request.attributes.get('env') que celui utilisé par l'expression, mais ici nous avons à disposition un raccourci qui nous est fourni par la variable globale "app.environment", ce qui rend le code plus concis.

{% if app.environment == 'dev' %} {# request.server.get('APP_ENV') // works too like in the expression #}
    <a class="dropdown-item" href="{{ path('easyadmin_dashboard') }}">{{ 'menu_admin'|trans }}</a>
{% endif %}

Conclusion

C'est un petit test que je voulais faire. Je préfère utiliser l'environnement à la place de l'IP. C'est plus robuste dans ce cas comme c'est une information "serveur" qui ne change pas : en production ce sera toujours "prod".

Et voilà ! J'espère que vous avez aimé. Découvrez d'autres informations en rapport à cet article avec les liens ci-dessous. Comme toujours, retours, likes et retweets sont les bienvenus. (voir la boîte ci-dessous) À tantôt ! COil. 😊

  Lire la doc  Plus sur Stackoverflow

  Travaillez avec moi !


A vous de jouer !

Ces articles vous ont été utiles ? Vous pouvez m'aider à votre tour de plusieurs manières : (cf le tweet à droite pour me contacter )

  • Me remonter des erreurs ou typos.
  • Me remonter des choses qui pourraient être améliorées.
  • Aimez et retweetez !
  • Suivez moi sur Twitter
  • Inscrivez-vous au flux RSS.
  • Cliquez sur les boutons Plus sur Stackoverflow pour me faire gagner des badges "annonceur" 🏅.

Merci d'avoir tenu jusque ici et à très bientôt sur Strangebuzz ! 😉

COil