-1

I have an application with slug. All routes will start with the same url. URL looks like myapp.com/company_slug/locale/.....

After I have submitted the login form, i have an error in my GuardAuthenticator to getLoginUrl(). Some mandatory parameters are missing ("company_slug") to generate a URL for route "app_login".

How i can have a company_slug in all route of my application and how to fix this bug ?

<?php

namespace App\Security;

[.....]

/**
 * Class LoginFormAuthenticator
 */
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
    use TargetPathTrait;

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;
    private $passwordEncoder;

    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->passwordEncoder = $passwordEncoder;
    }

    [....]

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }

        $url = $this->urlGenerator->generate('index');

        return new RedirectResponse($url);
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate('app_login');
    }
}

Controller where i would like to redirect to route "index" (/{company_slug}/{_locale}/index).

<?php

namespace App\Controller;

use App\Entity\Candidate;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * Class DefaultController.
 *
 * @Route("/{company_slug}/{_locale}")
 */
class DefaultController extends AbstractController
{
    /**
     * @Route("/", name="index")
     */
    public function index()
    {
        if ($this->getUser() instanceof User) {
            return $this->redirectToRoute('admin_index');
        } elseif ($this->getUser() instanceof Candidate) {
            return $this->redirectToRoute('candidate_index');
        } else {
            return $this->redirectToRoute('app_login');
        }
    }
}

Just in case, i setted an company attribute on kernel event.

<?php

namespace App\EventSubscriber;

use App\ContextStorage;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;

/**
 * Class AgencyContextSubscriber.
 */
class CompanyContextSubscriber implements EventSubscriberInterface
{
    /**
     * @var ContextStorage
     */
    private $contextStorage;

    public function __construct(ContextStorage $contextStorage)
    {
        $this->contextStorage = $contextStorage;
    }

    public function onKernelRequest(RequestEvent $event)
    {
        if (!$event->isMasterRequest()) {
            return;
        }

        $companySlug = $event->getRequest()->get('company_slug');

        if (!$companySlug) {
            return;
        }

        $company = $this->contextStorage->activateContextBySlug($companySlug);

        $event->getRequest()->attributes->set('company', $company);
    }

    public static function getSubscribedEvents()
    {
        return [
            'kernel.request' => 'onKernelRequest',
        ];
    }
}
Kévin
  • 39
  • 10

1 Answers1

1

You need to pass parameters array in $this->urlGenerator->generate('index'); it simply tries to generate your route but it sees that company_slug is missing so its unable to generate the url for you, in your CompanyContextSubscriber you're setting attribute on request try to retrieve it in onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey), try this:

$this->urlGenerator->generate('index', ['company_slug' => $request->attributes->get('company')]);

I believe you'll need to pass _locale param as well to the array above

B0re
  • 239
  • 1
  • 8
  • You're right but the problem is on getLoginUrl method. It has not Request parameter :/ – Kévin Jan 15 '20 at 07:58
  • you can always pass RequestStack in your CompanyContextSubscriber construct and get current request with it – B0re Jan 15 '20 at 08:13
  • Like this ? CompanyContextSubscriber ``` public function __construct(ContextStorage $contextStorage, RequestStack $requestStack) { $this->contextStorage = $contextStorage; $this->requestStack = $requestStack; } ``` And set CompanyContextSubscriber on LoginFormAuthenticator construct ? Or just set RequestStack as argument on LoginFormAuthenticator construct – Kévin Jan 15 '20 at 08:19
  • I'm sorry I missed the class I meant the authenticator, here is a pastebin with the code applied https://pastebin.com/bAkzYp8P – B0re Jan 15 '20 at 08:25
  • Ty BOre. Do you think it is a good way to make a CompanyContextSubscriber and add * @Route("/{company_slug}/{_locale}") to all my Controller ? Or are they an other solution to set a company_slug in on all my app ? – Kévin Jan 15 '20 at 08:37
  • I'd change routes/annotations.yml and in controllers add `prefix: /{_company_slug}/{_locale}` I think thats the preferred way of creating prefixes this should do the trick although not tested, instead of just copy pasting the prefix you need everywhere – B0re Jan 15 '20 at 08:43