Service of type Nette\Security\IAuthenticator not found

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

Z duvodu opraveneho bugu v Nette se snazim prejit na novou verzi. Prekopal jsem konfigurak a bootstrap a dostal se pres nejruznejsi chybove hlasky, az jsem se zasekl na teto:

Service of type Nette\Security\IAuthenticator not found.

Povetsinou z githubu a z dokumentace se snazim slozit spravnou konfiguraci Authenticatoru a configu, ale nedari se. Netusi nekdo prosim, kde je zakopany pes? Diky.

app/models/Authenticator.php:

<?php

namespace Model;

use Nette,
Nette\Utils\Strings;

class Authenticator extends Nette\Object implements NS\IAuthenticator
{
	/** @var Nette\Database\Connection */
        private $database;

	public function __construct(Nette\Database\Connection $database)
        {
        $this->database = $database;
        }


	/**
	 * Performs an authentication
	 * @param  array
	 * @return Nette\Security\Identity
	 * @throws Nette\Security\AuthenticationException
	 */
	public function authenticate(array $credentials)
	{
		list($username, $password) = $credentials;
                $row = $this->database->table('users')->where('username', $username)->fetch();

		if (!$row) {
			throw new NS\AuthenticationException("Špatná kombinace uživatelského jména a hesla.", self::IDENTITY_NOT_FOUND);
		}
                echo $this->calculateHash($password, $username);
		if ($row->password !== $this->calculateHash($password, $username) || $row->disabled==1) {
			throw new NS\AuthenticationException("Špatná kombinace uživatelského jména a hesla.", self::INVALID_CREDENTIAL);
		}

		unset($row->password);
		return new NS\Identity($row->id_user, "user", $row->toArray());
	}


        /**
         *
         * Gets username by his id.
         *
         * @param int $id id of user we want to know
         * @return string username
         */
        public static function getUsernameById($id)
        {
            return dibi::select('username')->from('users')->where('id_user=%i', $id)->fetchSingle();
        }

	/**
	 * Computes salted password hash.
	 * @param  string password
	 * @return string hash salted by username
	 */
	public static function calculateHash($password, $username)
	{
		return md5($password . 'salt' . $username);
	}
}
?>

app/config.neon:

php: # PHP configuration
            date.timezone: Europe/Prague
            # session.save_path: "%tempDir%/sessions"
            # zlib.output_compression: yes
nette:
    debugger:
        strictMode: true
        bar:
            - @sessionPanel
    session:
        autoStart: smart
        expiration: +5 days

services:
        robotLoader:
            run: true

        database:
            class: Nette\Database\Connection('mysql:host=localhost;dbname=databaze', 'user', 'pass')

        model:
            class: Model
            arguments: [@database]

        authenticator:
          create: Authenticator( @database )

        polozkyOpravy:
            class: PolozkyOpravy
            arguments: [@session]

        sessionPanel:
            class: SessionPanel
            arguments:
                - @application
                - @session


factories:

development:
  parameters:
    language: 'cs'
    currency: 'Kč'
    database:
        driver: mysql
        host: localhost
        database: databaze
        username: user
        password: pass
        profiler: TRUE
        charset: utf8
vvoody
Člen | 910
+
0
-
	authenticator:
		class: Authenticator( @database )
mistm
Člen | 25
+
0
-

Zkusil jsem, ale porad to same. Doplnim jeste, ze pouzivam aktualni verzi z webu – tzn. Nette Framework 2.0.8 stabilní, uvolněný 1. 1. 2013. Chybu ukazuje ladenka v Nette\Security\User.php:177:

return $this->authenticator ?: $this->context->getByType('Nette\Security\IAuthenticator');

V call stacku vidim posledni muj kod v SignPresenter.php v metode odeslani prihlasovaciho formulare:

$this->getUser()->login($values->username, $values->password);

Jeste jedna poznamka – musel jsem vypnout nastaveni delky session pro uzivatele tzn:

if ($values->remember) {
	$this->getUser()->setExpiration('+ 14 days', FALSE);
} else {
	$this->getUser()->setExpiration('+ 1 day', TRUE);
}

Protoze mi to hazelo vyjimku: The expiration time is greater than the session expiration 10800 seconds. Pritom session v konfiguraku nastavuji (prebral jsem to tusim z dokumentace, protoze jsem to tam predtim nemel). Mozna by to mohlo vest k nalezeni problemu.

Editoval mistm (20. 1. 2013 14:26)

vvoody
Člen | 910
+
0
-
class Authenticator extends Nette\Object implements Nette\Security\IAuthenticator
mistm
Člen | 25
+
0
-

To by znelo logicky, nicmene nepomohlo. Pro jistotu jsem v kodu napsal napevno vsude Nette\Security\IAuthenticator:

<?php

namespace Model;

use Nette,
Nette\Utils\Strings;

class Authenticator extends Nette\Object implements Nette\Security\IAuthenticator
{
	/** @var Nette\Database\Connection */
        private $database;

	public function __construct(Nette\Database\Connection $database)
        {
        $this->database = $database;
        }


	/**
	 * Performs an authentication
	 * @param  array
	 * @return Nette\Security\Identity
	 * @throws Nette\Security\AuthenticationException
	 */
	public function authenticate(array $credentials)
	{
		list($username, $password) = $credentials;
		//$row = $this->users->where('username', $username)->fetch();
                $row = $this->database->table('users')->where('username', $username)->fetch();

		if (!$row) {
			throw new Nette\Security\AuthenticationException("Špatná kombinace uživatelského jména a hesla.", self::IDENTITY_NOT_FOUND);
		}
                echo $this->calculateHash($password, $username);
		if ($row->password !== $this->calculateHash($password, $username) || $row->disabled==1) {
			throw new Nette\Security\AuthenticationException("Špatná kombinace uživatelského jména a hesla.", self::INVALID_CREDENTIAL);
		}

		unset($row->password);
		return new Nette\Security\Identity($row->id_user, "user", $row->toArray());
	}


        /**
         *
         * Gets username by his id.
         *
         * @param int $id id of user we want to know
         * @return string username
         */
        public static function getUsernameById($id)
        {
            return dibi::select('username')->from('users')->where('id_user=%i', $id)->fetchSingle();
        }

	/**
	 * Computes salted password hash.
	 * @param  string password
	 * @return string hash salted by username
	 */
	public static function calculateHash($password, $username)
	{
		return md5($password . 'salt' . $username);
	}

}
?>
castamir
Člen | 629
+
0
-

třídu Authenticator máš v namespace Model, což se musí projevit i v configu.

authenticator:
    class: Model\Authenticator( @database )

Editoval castamir (20. 1. 2013 14:48)

mistm
Člen | 25
+
0
-

Pro jistotu jeste uvadim bootstrap.php

<?php

use Nette\Diagnostics\Debugger, Nette\Application\Routers\Route;
use Nette\Forms\Container;

// Load Nette Framework
$params['libsDir'] = __DIR__ . '/../libs';
require $params['libsDir'] . '/Nette/loader.php';

// Enable Nette Debugger for error visualisation & logging
Debugger::$logDirectory = __DIR__ . '/../log';
Debugger::$strictMode = TRUE;
Debugger::enable();

require $params['libsDir'] . '/dibi/dibi.php';
// Load configuration from config.neon file
/*$configurator = new Nette\Configurator;
$configurator->container->params += $params;
$configurator->container->params['tempDir'] = __DIR__ . '/../temp';
$container = $configurator->loadConfig(__DIR__ . '/config.neon');*/

$configurator = new Nette\Config\Configurator;
$configurator->setTempDirectory(__DIR__ . '/../temp');
$configurator->createRobotLoader()
    ->addDirectory(__DIR__)
    ->addDirectory($params['libsDir'])
    ->register();
$configurator->addParameters($params);
$configurator->addConfig(__DIR__ . '/config.neon');
$container = $configurator->createContainer();

dibi::connect( $container->params['database'] );

// Setup router
$router = $container->router;
$router[] = new Route('index.php', 'Sign:in', Route::ONE_WAY);
$router[] = new Route('', 'Sign:in');
$router[] = new Route('<presenter>/<action>[/<id>]', 'Sign:');

Container::extensionMethod('addDatePicker', function (Container $container, $name, $label = NULL) {
    return $container[$name] = new JanTvrdik\Components\DatePicker($label);
});

\Nette\Diagnostics\Debugger::$bar->addPanel(new SessionPanel($container->session));

// Configure and run the application!
$application = $container->application;
//$application->catchExceptions = TRUE;
$application->errorPresenter = 'Error';
$application->run();
mistm
Člen | 25
+
0
-

Zkusil jsem dat namespace pryc i zmenit v konfiguraku Model\Authenticator a nepomohlo :-/

Problem je jestli to dobre chapu s nacitanim tridy Authenticator – tzn neco s RobotLoaderem?

castamir
Člen | 629
+
0
-

pro jistotu zkus promazat cache

mistm
Člen | 25
+
0
-

To jsem take zkousel :-) Ted pro jistotu znovu a porad nic. Nemusi se RobotLoader s novym Configuratorem nejak specialne spoustet?

castamir
Člen | 629
+
0
-

Ne, dokonce i službu robotLoader není potřeba v konfiguráku uvádět.

castamir
Člen | 629
+
0
-

Kde přesně ti to zařve? Při volání metody User::login?

Editoval castamir (20. 1. 2013 15:12)

mistm
Člen | 25
+
0
-

Promin, ze jsem tak dlouho nereagoval. Nevsiml jsem si odpovedi.

V mem kodu se posledni vola: $this->getUser()->login($values->username, $values->password);

public function signInFormSubmitted($form)
{
   try {
      $values = $form->getValues();
      $this->getUser()->login($values->username, $values->password);
      $this->redirect('Hlidac:default');
   } catch (NS\AuthenticationException $e) {
      $form->addError($e->getMessage());
      $this->redirect('Sign:in');
   }
}

V call stacku pak dal vidim, ze to skonci na Nette/Security/User.php:177:

final public function getAuthenticator()
{
   return $this->authenticator ?: $this->context->getByType('Nette\Security\IAuthenticator');
}
vvoody
Člen | 910
+
0
-

Dumpni si context a vizuálne tam pohľadaj ten Authenticator.

mistm
Člen | 25
+
0
-

Neni tam.

vvoody
Člen | 910
+
0
-

Tak ešte raz poprosím:

  • definíciu služby authenticator v configu
  • obsah property public $classes vo vygenerovanom DIC
  • celu metodu createServiceAuthenticator z vygenerovaného DIC ak tam existuje
mistm
Člen | 25
+
0
-

vvoody napsal(a):

  • definíciu služby authenticator v configu
services:
        authenticator:
          class: Authenticator( @database )
  • obsah property public $classes vo vygenerovanom DIC

Doufam, ze se to vypise takto… volam print_r($this->getContext()->classes); tesne pred mym radkem, kde to pada.

Array (     [nette\object] =>      [nette\caching\storages\ijournal] => nette.cacheJournal     [nette\caching\storages\filejournal] => nette.cacheJournal     [nette\caching\istorage] => cacheStorage     [nette\caching\storages\filestorage] => cacheStorage     [nette\http\requestfactory] => nette.httpRequestFactory     [nette\http\irequest] => httpRequest     [nette\http\request] => httpRequest     [nette\http\iresponse] => httpResponse     [nette\http\response] => httpResponse     [nette\http\context] => nette.httpContext     [nette\http\session] => session     [nette\security\iuserstorage] => nette.userStorage     [nette\http\userstorage] => nette.userStorage     [nette\security\user] => user     [nette\application\application] => application     [nette\application\ipresenterfactory] => nette.presenterFactory     [nette\application\presenterfactory] => nette.presenterFactory     [nette\arraylist] => router     [traversable] => router     [iteratoraggregate] => router     [countable] => router     [arrayaccess] => router     [nette\application\irouter] => router     [nette\application\routers\routelist] => router     [nette\mail\imailer] => nette.mailer     [nette\mail\sendmailmailer] => nette.mailer     [nette\di\nestedaccessor] => nette.database     [nette\freezableobject] => container     [nette\ifreezable] => container     [nette\di\icontainer] => container     [nette\di\container] => container )
  • celu metodu createServiceAuthenticator z vygenerovaného DIC ak tam existuje

Tady nevim presne oc me zadas. Nejsem na Nette zrovna specialista :-) Ve svem kodu zadnou metodu createServiceAuthenticator nemam. Pokud ji tam mam mit, tak tam muze byt problem.

jiri.pudil
Nette Blogger | 1029
+
0
-

Metodu createServiceAuthenticator hledej v temp/cache/_Nette.Configurator/*

vvoody
Člen | 910
+
0
-

Je to hodne divné, podla tej property classes to vyzerá tak akoby si mal prázdny config, respektíve tvoj config nebol predaný configuratoru. Chýbajú tam aj ďalšie služby ktoré máš v configu (PolozkyOpravy, Model)

Teraz pozerám na:

$configurator->addConfig(__DIR__ . '/config.neon');

nemáš ho náhodou v zložke?

$configurator->addConfig(__DIR__ . '/config/config.neon');

To by síce kričalo že ten __DIR__ . ‚/config.neon‘ neexistuje, ale možno aj existuje a je prázdny.

mistm
Člen | 25
+
0
-

Tak tedy createServiceAuthenticator jsem v _Nette.Configurator/cosi.php vubec nenasel.

config.neon mam primo v app. Do bootstrap.php jsem si zkusil pridat

echo __DIR__ . '/config.neon';

a dostal jsem D:\xampp\htdocs\HlidacObjednavek\app/config.neon – coz je presne cesta, kde je config.neon ulozen.

Nemusi byt v tom .neon presny odsazeni? Napr na radku za services: ma byt prave 1× tab (tzn nesmi byt 2x)? Nebo neni tam nejaky znak pro viceradkovy komentar (jakoze je moje konfigurace zakomentovana)? Ostatne .neon je vyse.

Editoval mistm (29. 1. 2013 3:05)

mistm
Člen | 25
+
0
-

Config to urcite nacita, protoze v tom _Nette.Configurator/cosi.php jsem nasel nasledujici kod, kde je mnou definovane currency a language.

public function __construct()
{
	parent::__construct(array(
		'appDir' => 'D:\\xampp\\htdocs\\HlidacObjednavek\\app',
		'wwwDir' => 'D:\\xampp\\htdocs\\HlidacObjednavek\\document_root',
		'debugMode' => TRUE,
		'productionMode' => FALSE,
		'environment' => 'development',
		'consoleMode' => FALSE,
		'container' => array(
			'class' => 'SystemContainer',
			'parent' => 'Nette\\DI\\Container',
		),
		'tempDir' => 'D:\\xampp\\htdocs\\HlidacObjednavek\\app/../temp',
		'libsDir' => 'D:\\xampp\\htdocs\\HlidacObjednavek\\app/../libs',
		'language' => 'cs',
		'currency' => 'Kč',
		'database' => array(
			'driver' => 'mysql',
			'host' => 'localhost',
			'database' => 'databaze',
			'username' => 'user',
			'password' => 'pass',
			'profiler' => TRUE,
			'charset' => 'utf8',
		),
	));
}

Editoval mistm (29. 1. 2013 3:13)

vvoody
Člen | 910
+
0
-

Hej zdá sa že to odsadenie máš zle. Všetko nad blokom factories odsaď o tabulátor a na začiatok configu pridaj na úroveň factories blok common (0× tabulator)

common:
	parameters:

	php:

	nette:

	services:

	factories:

production < common:

development < common:
mistm
Člen | 25
+
0
-

Zkusil jsem .neon prekopat podle .neonu v sanboxu a podle toho co pises. Chyba uz je jina:

Found sections 'database' in configuration, but corresponding extensions are missing.

Vzpominam si, ze jsem tuto hlasku uz videl pri prechodu na novou verzi. Hledal jsem na foru a vysel mi z toho ten config vys, ktery „fungoval“, protoze se tam ty veci nenacitaly :-) Cili byla chyba opravdu asi v nem. Jen nevim co ted s timto. Pouzivam dibi.

common:
  php: # PHP configuration
    date.timezone: Europe/Prague
    # session.save_path: "%tempDir%/sessions"
    # zlib.output_compression: yes
  nette:
    debugger:
      strictMode: true
      bar:
        - @sessionPanel
    session:
      autoStart: smart
      expiration: +5 days
  services:
    robotLoader:
      run: true
    database:
      class: Nette\Database\Connection('mysql:host=localhost;dbname=databaze', 'user', 'pass')
    model:
      class: Model
      arguments: [@database]
    authenticator:
      class: Authenticator( @database )
    polozkyOpravy:
      class: PolozkyOpravy
      arguments: [@session]
    sessionPanel:
      class: SessionPanel
      arguments:
        - @application
        - @session
  factories:
  parameters:
    language: 'cs'
    currency: 'Kč'

development < common:
  database:
    driver: mysql
    host: localhost
    database: databaze
    username: user
    password: pass
    profiler: TRUE
    charset: utf8

production < common:
  database:
    driver: mysql
    host: localhost
    database: databaze
    username: user
    password: pass
    profiler: TRUE
    charset: utf8

Zkusil jsem config jako je zde https://forum.nette.org/…di-container#… a dostal jsem tu samou hlasku.

Tomas Jancik
Člen | 103
+
0
-

parametry pro pripojeni k databazi zanor do sekce parameters

development < common:
  parameters:
    database:
      driver: mysql
      host: localhost
      database: databaze
      username: user
      password: pass
      profiler: TRUE
      charset: utf8

production < common:
  parameters:
    database:
      driver: mysql
      host: localhost
      database: databaze
      username: user
      password: pass
      profiler: TRUE
      charset: utf8
mistm
Člen | 25
+
0
-

Diky. Tahle chyba opravdu zmizela. Ted jsem tedy na:

Class and factory are missing in service 'robotLoader' definition.
Tomas Jancik
Člen | 103
+
0
-

snazis se vytvorit service robotLoader, ale nespecifikujes odkud se ma vzit (ktera trida nebo tovarnicka)

jak uz tady psal @castamir, robotLoader bys v configu vubec nemusel definovat

mistm
Člen | 25
+
0
-

Kdyz ho dam pryc, tak mi to hazi:

Service 'authenticator': Reference to missing service 'database'.

Jinak tedy dekuju za trpelivost :-) Uz jsem byl opravdu zoufalej.

castamir
Člen | 629
+
0
-

bud to napises ve zkracene notaci, nebo ve zdlouhave, rozhodne bych to nekombinoval

zdlouhavy zapis

authenticator:
  class: Authenticator
  arguments: [@database]

zkraceny

authenticator: Authenticator

zkraceny s explicitnim nastavenim parametru

authenticator: Authenticator ( @database )

Vyzkousej a dej vedet

Editoval castamir (29. 1. 2013 13:21)

mistm
Člen | 25
+
0
-

Zkusil jsem vsechny 3 varianty a porad to same. Pri zkracenem zapisu authenticator: Authenticator to hazi

Service 'authenticator': No service of type Nette\Database\Connection found. Make sure the type hint in Method Authenticator::__construct() is written correctly and service of this type is registered.

Ja v config.neon ale zadnou service Connection nemam.

Tomas Jancik
Člen | 103
+
0
-

z tech hlasek bych soudil, ze se nevytvari service database…

mistm
Člen | 25
+
0
-

Zkusil jsem tedy pridat do configu k services podle dokumentace toto:

connection:
  class: DibiConnection(%database%)

Porad ale dostavam onu hlasku No service of type Nette\Database\Connection found.

Editoval mistm (29. 1. 2013 17:12)

Badaboom
Člen | 33
+
0
-

Protože autowiring očekává (přes typehint v contructoru Authenticatoru) Nette\Database\Connection, ale ty registruješ DibiConnection.

Buď přepiš Authenticator na použití Dibi nebo místo Dibi použij Nette\Database.

Editoval Badaboom (29. 1. 2013 18:06)

mistm
Člen | 25
+
0
-

Upravil jsem tedy konstruktor na public function __construct(DibiConnection $database)

Pak jsem jeste zmenil z…

model:
  class: Model
  arguments: [@database]
sessionPanel:
  class: SessionPanel
  arguments:
    - @application
    - @session

… na …

model:
  class: Model
  arguments: [%database%]
sessionPanel:
  class: SessionPanel
  arguments:
    - @session

Ted mi ale rve \libs\dibi\libs\DibiObject.php

Call to undefined method DibiConnection::table().

Chapu to dobre, ze injektuju DibiConnection necemu, co ocekava nejakej interface Connection, kterej ma mit metodu table()?

Editoval mistm (29. 1. 2013 18:23)

castamir
Člen | 629
+
0
-

už se blížíme. Zkusím sem psotnout jednoduchý config, který jsem si před chvilkou vyzkoušel. Snad se ti to podle toho podaří opravit.

common:
  parameters:

  php:
    date.timezone: Europe/Prague

  nette:
    application:
      errorPresenter: Front:Error

    session:
      expiration: 14 days

  services:
    authenticator: Authenticator ( @dibi.connection )

    users: Users ( @dibi.connection )

  factories:

  dibi:
      driver: %database.driver%
      charset: utf8
      lazy: true
      host: %database.host%
      username: %database.user%
      password: %database.password%
      database: %database.dbname%
      profiler: true
      result:
          detectTypes: true

production < common:

development < common:
  parameters:
    database:
      driver: mysql
      host: localhost
      dbname: mydbname
      user: username
      password:
Badaboom
Člen | 33
+
0
-

DibiConnection nemá metodu table(). Ty ji ale používáš. Ten Authenticator výše je hybrid, který používá Dibi i Nette\Database.

Např. tohle je zápis pro Nette\Database:

$row = $this->database->table('users')->where('username', $username)->fetch();

Přepiš to na Dibi.

Editoval Badaboom (29. 1. 2013 18:32)

mistm
Člen | 25
+
0
-

Hura slava. Dekuju vsem. Konecne se podarilo. Opravdu to bylo tim, ze jsem odnekud zkopiroval $this->database->table(‚users‘)… Vratil jsem puvodni kod, ktery jsem jeste musel trochu upravit, a zase vse funguje. Dekuju vam vsem vazne moc! :-)