Nefunguje inject autentikátoru do presenteru

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Džůny
Člen | 19
+
0
-

Ahoj, mám aplikaci rozdělenou na dva moduly, Admin a Front, a napsal jsem si dva autentikátory. Bůhvíproč mi ale v Presenteru nejdou injectnout.

Třída FrontAuthenticator:

namespace App\Model\Security;

use Nette,
    App\Model\BaseFacade;

/**
 * Front authenticator class.
 */
class FrontAuthenticator extends BaseFacade implements Nette\Security\IAuthenticator
{
    /** @var string */
    protected $table = 'users';


    /**
     * Performs an authentication.
     * @return Nette\Security\Identity
     * @throws Nette\Security\AuthenticationException
     */
    public function authenticate(array $credentials)
    {
	    …
    }
}

V configu mám:

services:
	frontAuthenticator: {class: App\Model\Security\FrontAuthenticator, autowired: false}

A v BasePresenteru si ho chci injectnout:

/** @var \App\Model\Security\FrontAuthenticator @inject */
public $frontAuthenticator;

Jenže pořád dokola dostávám jen:

Nette\InvalidStateException

Service of type App\Model\Security\FrontAuthenticator used in @var annotation at App\Front\Presenters\BasePresenter::$frontAuthenticator not found. Did you register it in configuration file?

Musím to udělat jinak, předáním přes konstruktor presenteru? Chtěl jsem se konstruktoru vyhnout, kvůli constructor hell v potomcích BasePresenteru…

- App\Front\Presenters\BasePresenter(@App\Model\Security\FrontAuthenticator)

Nevím kde může být chyba. Namespace mám snad dobře, v Containeru se normálně vytvoří:

'frontAuthenticator' => 'App\Model\Security\FrontAuthenticator',

Předem díky za každou radu…

Edit.
Nette 2.3.4, cache jsem smazal.

Editoval Džůny (26. 7. 2015 14:07)

Matey
Člen | 142
+
0
-

Ahoj,
nedávno som tiež potreboval osobitný authenticator na front/admin a tu sa ponúka jednoduché riešenie :)

nette-multi-authenticator

proste sa vyhneš IAuthenticatoru

ešte kompletná ukážka:

<?php

namespace App\Model;

use Kdyby\Doctrine\EntityManager;
use Nette\Object;
use Nette\Security;

class FrontAuthenticator extends Object
{
	/**
	 * @var EntityManager
	 */
	private $entityManager;

	/**
	 * @var Security\User
	 */
	private $user;

	/**
	 * @param EntityManager $entityManager
	 * @param Security\User $user
	 */
	public function __construct(EntityManager $entityManager, Security\User $user)
	{
		$this->entityManager = $entityManager;
		$this->user = $user;
	}

	/**
	 * @param $email
	 * @param $password
	 * @throws Security\AuthenticationException
	 */
	public function login($email, $password)
	{
		$customer = $this->entityManager->getRepository(Customer::class)->findOneBy(['email' => $email]);
		if (!$customer) {
			throw new Security\AuthenticationException('Bad EMAIL', Security\IAuthenticator::IDENTITY_NOT_FOUND);
		} elseif (!Security\Passwords::verify($password, $customer->password)) {
			throw new Security\AuthenticationException('Bad PASSWORD', Security\IAuthenticator::INVALID_CREDENTIAL);
		} elseif (Security\Passwords::needsRehash($customer->password)) {
			$customer->setPassword($password);
			$this->entityManager->flush();
		} else {
			$this->user->getStorage()->setNamespace('Front');
			$this->user->login(new Security\Identity($customer->id, [$customer->role]));
		}
	}
}
?>

a zaregistrovať ako službu

Šaman
Člen | 2666
+
0
-

Problém je v tom autowired: false. Jestli nikde neinjectuješ obecný Nette\Security\IAuthenticator, tak zruš vypnutí autowired a injectuj si až konkrétní instance (FrontAuthenticator, AdminAuthenticator).

Zax
Člen | 370
+
0
-

@Šaman To je právě hroznej opruz, náhodou to bude chtít nějaká knihovna nebo něco a už to musíš předělávat. Mně v Nette trochu chybí možnost zapnout/vypnout autowire zvlášť podle classy nebo podle interfacu. Řešení co postnul @Matey je IMO pro tenhle případ nejlepší, prostě si napsat vlastní služby, ty vyžadovat, neřešit interface.

Džůny
Člen | 19
+
0
-

@Matey Díky za odkaz a ukázku! :) Vyhnout se IAuthenticatoru bude asi nejsnazší.

@Šaman Áha. Díky za vysvětlení. Když zruším vypnutí autowired, tak mi Laděnka hlásí:

Nette\DI\ServiceCreationException

Service 'security.user': Multiple services of type Nette\Security\IAuthenticator found: adminAuthenticator, frontAuthenticator

…což je správně, protože jak FrontAuthenticator tak AdminAuthenticator implementují IAuthenticator. Už jsem z toho zmatený. Vyřeším to tak jak psal @Matey.

Editoval Džůny (26. 7. 2015 14:46)

Šaman
Člen | 2666
+
0
-

Na usera jsem zapomněl. Za sebe musím říct, že mi tahle provázanost uživatel – autentikátor – $presenter->user a $user v šabloně docela vadí.

Pak je druhá možnost – nechat ten sekundární autentikátor autowired: false, ale do FrontPresenteru (nebo i všech jeho potomků) ho injectuješ ručně. Tedy na straně presenteru si vytvoříš setter ((set|inject)Autenticator) a zavoláš ho z configu pomocí decoratoru

decorator:
	App\Front\Presenters\BasePresenter:
		setup:
			- injectAutenticator(@App\Model\Security\FrontAuthenticator)
Džůny
Člen | 19
+
0
-

Zezačátku mi to připadalo dost matoucí, než jsem si v API našel, že jsou to automaticky předávané magické property a všechno okolo toho. Ono stejně teď po rozdělení UserStorage podle NS Admin/Front si musím v BasePresenteru předávat $this->template->user = $this->getUser() ručně a metodu getUser() mít přetíženou:

public function getUser()
{
	    $user = parent::getUser();
	    $user->getStorage()->setNamespace('Front');
	    return $user;
}

Asi by se mi líbilo, kdyby šel celý problém řešit nějak pěkněji, jen mě nenapadá jak.

Každopádně děkuji za rady, už jsem to přepsal a funguje to pěkně.

Edit. Mimochodem, kde se všichni naučili ta kouzla s configem? :) Stačí více porozumět Nette a principu vytváření Containteru a ono to půjde samo?

Editoval Džůny (26. 7. 2015 18:42)

Myiyk
Člen | 321
+
0
-

@Džůny na ty kouzla bys musel být autor nebo přečíst alespoň polovinu fóra.
Já ty pokročilé věci taky neumím. Nebo si myslím že umím ale nefungují.

Jak jsi teda vyřešil ten problém?

Džůny
Člen | 19
+
0
-

Tak jak to napsal @Matey, jde to poskládat dohromady z toho odkazu na nette-multi-authenticator a ukázky kterou napsal.

Ještě kdyby někdo potřeboval uživatele „schovaného“ v Namespace dostat do komponenty přes DI a nešlo mu to:

public function __construct(\Nette\Security\User $user)
{
		$this->user = $user->getStorage()->setNamespace('Front');
}

	private function doSomething()
	{
		$this->user->identity…
	}

Je potřeba ten Namespace znovu nastavit.

Nebo by šlo komponentu zdědit od UI\Control, pokud už není, a dostat se přes $this->getPresenter() na přetíženou metodu getUser() (můj předchozí post), ale to mi připadá nečisté (poruší se tím princip DI?).

Editoval Džůny (27. 7. 2015 1:55)

Pavel Janda
Člen | 977
+
0
-

Přetěžování metod znamená něco jiného. PHP neumožňuje přetěžovat metody v tom klasickém slova smyslu. Používá sice termín „class methods overloading“, ale pro něco (implementačně) jiného.

Džůny
Člen | 19
+
0
-

Pardon, tak tedy přepsanou, ať jsme přesní.

Ještě prosím někoho o vysvětlení: když si injectnu Nette\Security\User do různých komponent, proč v některých Namespace na Front nastavený je, a v některých ne? Asi mi pořád něco uniká…

Edit: Už jsem asi našel téma které toto řeší.

Editoval Džůny (27. 7. 2015 22:11)