symfonysulu

How to require login for a custom controller?


we are implemented a custom controller for one webspace of our sulu 2.6 installation. It requires login against our SSO, but the problem is, that the result is cached.

We have 4 Webspaces, just one should have this login protection. I found now also this article on User Context Caching but it appears to enable it for all webspaces.

Here is the current controller code

<?php

namespace App\Controller\Website;

use App\Service\SiteService;
use DWBN\VanillaSsoAuthentication\OAuth\AccessDeniedException;
use DWBN\VanillaSsoAuthentication\OAuth\ApplicationAccessDeniedException;
use Exception;
use Sulu\Bundle\WebsiteBundle\Controller\WebsiteController;
use Sulu\Component\Content\Compat\StructureInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use DWBN\VanillaSsoAuthentication\OAuth\OAuth2;
use Symfony\Component\Routing\Annotation\Route;

class SsoProtectedController extends WebsiteController
{

    public function __construct(
        private readonly SiteService $siteParameterService,
    ) {}

    public function indexAction(StructureInterface $structure, $preview = false, $partial = false): Response  // (3)
    {

        if (!$preview && !$partial) {
            // initalize the sso
            $this->initSso();

            $user = OAuth2::get_user();

            if (!$user->isAuthenticated()) {

                $extra_state = array(
                    'remember_me' => true,
                    'next' => $this->container->get('request_stack')->getCurrentRequest()->getPathInfo()
                );

                $authentication_uri = OAuth2::get_authentication_uri( 'page', $extra_state);

                return $this->redirect($authentication_uri);
            }
        }


        return $this->renderStructure(
            $structure,
            [],
            $preview,
            $partial
        );
    }

    #[Route(path: '/sso-login')]
    public function loginAction(): Response
    {
        $error_message = null;

        try {
            $this->initSso();
            OAuth2::login(true);
        } catch (AccessDeniedException $e) {
            $error_message = 'Authentication cancelled';
        } catch (ApplicationAccessDeniedException $e) {
            $error_message = 'Authorisation failed: ' . $e->getMessage();
        } catch (Exception $e) {
            $error_message = 'ERROR: ' . $e->getMessage();
        }

        // Render error template
        return $this->render('mh_sso_login.html.twig', [
            'error_message' => $error_message,
            'request' => $this->container->get('request_stack')->getCurrentRequest(),
        ]);
    }

    #[Route(path: '/sso-logout')]
    public function logoutAction()
    {
        $this->initSso();
        OAuth2::logout();
        $redirectTo = array_key_exists('next', $_GET) ? $_GET['next'] : '/';
        return $this->redirect($redirectTo);
    }



    private function initSso(): void
    {
        $config = $this->siteParameterService->getSiteKey('sso', ['host' => 'https://sso-dev.default-url.org']);
        $config['login_uri'] = $this->generateUrl('app_website_ssoprotected_login');
        $config['logout_uri'] = $this->generateUrl('app_website_ssoprotected_logout');
        $config['data_dir'] = $this->getParameter('kernel.project_dir') . '/var/cache/sso';
        OAuth2::init($config, $config['host']);
    }
}

I guess we have to integrate more closely with the symfony authorization mechanism? I found this cookbook entry but I don't fully understand it and it also feels a bit complicated. I only want to assure a binary logged-in or not protection and not create a lot of users in the sulu backend.

Any finger point in the right direction is highly appreciated.


Solution

  • In addition to @Alexanders answer, I managed to limit the logiin to a single webspace via this way:

    config/packages/security.yaml

    security:
      providers:
        sulu:
          id: sulu_security.user_provider
        sso_session:
          id: App\Security\SsoSessionProvider
      access_control:
        # ...
        - { path: ^/permission-denied, roles: [PUBLIC_ACCESS, IS_AUTHENTICATED_ANONYMOUSLY] }
        - { path: ^/sso-login, roles: [PUBLIC_ACCESS, IS_AUTHENTICATED_ANONYMOUSLY] }
        - { path: ^/, roles: FRONTEND_USER, host: '^(dev\.|www\.)?this-webspace(\.localhost|\.local|\.org)$' }
    
     # ... all the rest
    
     # and a dedicated setup for this single webspace, with host mapping
     mahamudra_holders:
        host: '^(dev\.|www\.)?this-webspace(\.localhost|\.local|\.org)$'
        pattern: ^/
        custom_authenticators:
          - App\Security\SsoSessionAuthenticator
        provider: sso_session
        lazy: true
    

    The SsoSessionAuthenticator is written as custom authenticator, as we don't want sulu users for all of them.