You cannot serialize or unserialize PDO instances

Notice: This thread is very old.
Reka
Member | 19
+
0
-

Hello,

I am unable to understand what is something wrong with my authentification script.

I have followed this documentation, but it returns the same error whatever I do, for the line $user->login($username, $password);.
The error is : “PDOException – You cannot serialize or unserialize PDO instances”

Here is my whole code :

<?php

namespace App\Presenters;

use Nette,
	App\Forms\SignFormFactory,
	App\Presenters\Passwords,
	Nette\Application\UI\Form,
	Nette\Database\Connection,
	Nette\Security as NS;


class ConnexionPresenter extends BasePresenter
{
    /** @var User */
    public $user;
	/** @var SignFormFactory @inject */
	public $factory;
	/** @var Nette\Database\Context */
	private $database;

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

	}
/*	public function __construct(Nette\Database\Context $database, User $user)
	{
		$this->database = $database;
		$this->user = $user;
	}
*/

	protected function createComponentConnexionFormulaire()
	{
		$form = new Form;

		$form->addText('identifiant', 'Identifiant :')
		->setRequired('Type your nickname or email address.')
		->setAttribute('placeholder', 'Pseudonyme ou E-mail');

		$form->addPassword('password', 'Mot de passe :')
		->setRequired('Type your password.')
		->setAttribute('placeholder', 'Mot de passe');

		$form->addSubmit('send', 'Connexion');

		$form->onSuccess[] = array($this, 'connexionFormulaireSucces');

		$renderer = $form->getRenderer();
		$renderer->wrappers['controls']['container'] = NULL;
		$renderer->wrappers['pair']['container'] = NULL;
		$renderer->wrappers['label']['container'] = NULL;
		$renderer->wrappers['control']['container'] = NULL;

		return $form;
	}

	public function connexionFormulaireSucces($form, $values)
	{
		$identifiant = strip_tags(trim($values->identifiant));
		$mdp = trim($values->password);

		$is_membre = $this->database->table('membres')->where('nick = ? OR email = ?', $identifiant, $identifiant)->fetch();
		$user = $this->getUser() ;
		dump($user->login($identifiant, $mdp)); // PDO exception
		echo $user->isLoggedIn() ? 'yes' : 'no';
		die(dump($is_membre->isLoggedIn() ? 'yes' : 'no'));

		if($is_membre !== FALSE) {
			if($is_membre->activation === 1) {
				if(!NS\Passwords::verify($mdp, $is_membre->password)) {
					$this->flashMessage('Nickname or password is wrong.', 'failure');
				} else {
					$this->flashMessage('OK.', 'success');
					$user = $this->getUser();
					$user->login($is_membre->nick, $mdp); // PDO exception
					echo $user->isLoggedIn() ? 'yes' : 'no';
				}
			} else {
				$this->flashMessage('A mail was sent to you to confirm your subscription, please check your inbox.', 'failure');

			}
		} else {
			$this->flashMessage('You are not subscribed', 'failure');
		}
	}
}

Furthermore, I changed the table name and column name under app/model/UserManager (adaptation to my DB reality), but it still go wrong.

Initially, I did all the work by myself (the reason why my code seems """a little bit""" redundant), but if I want to use Oauth and social network connexion it will be complicated. I try to understand the simple way suggested by Nette, but it is more difficult than I thought.

Could you help me, please ?
Thanks in advance.

Last edited by Reka (2015-12-06 21:38)

Milo
Nette Core | 1175
+
0
-

The code seems OK. How the exception trace looks like?

Reka
Member | 19
+
0
-

Hello Milo,

The Tracy exception looks like this scary red page …

greeny
Member | 405
+
+1
-

In Autentizator, you are passing whole Row to identity as data. Try using iterator_to_array($row);

Last edited by greeny (2015-12-07 13:42)

Reka
Member | 19
+
0
-

Hello Greeny,
Where? which line is concerned?

Like this? :/ (completely unsure to understand you…) :

if(!NS\Passwords::verify($mdp, $is_membre->password)) {
   $this->flashMessage('Vos identifiant et mot de passe ne coïncident pas.', 'failure');
} else {
   $this->flashMessage('YOUPI.', 'success');
   $user = $this->getUser();
   $user = iterator_to_array($user); // Greeny tip ?
   $user->login($is_membre->pseudo, $mdp);
   echo $user->isLoggedIn() ? 'yes' : 'no';
}

Result : Recoverable Error – Argument 1 passed to iterator_to_array() must implement interface Traversable, instance of Nette\Security\User given

I didn't read that kind of code in other examples :
Garry or Paolo

greeny
Member | 405
+
0
-

Show us code from Authentizator so we can point you to right direction

Reka
Member | 19
+
0
-

But where is Authentizator file? Do you mean IAuthenticator?

<?php

/**
 * This file is part of the Nette Framework (http://nette.org)
 * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
 */

namespace Nette\Security;


/**
 * Performs authentication.
 */
interface IAuthenticator
{
	/** Credential key */
	const USERNAME = 0,
		PASSWORD = 1;

	/** Exception error code */
	const IDENTITY_NOT_FOUND = 1,
		INVALID_CREDENTIAL = 2,
		FAILURE = 3,
		NOT_APPROVED = 4;

	/**
	 * Performs an authentication against e.g. database.
	 * and returns IIdentity on success or throws AuthenticationException
	 * @return IIdentity
	 * @throws AuthenticationException
	 */
	function authenticate(array $credentials);

}

Or do you want my UserManager under App/Model/ (which implements IAuthenticator) ?

<?php

namespace App\Model;

use Nette;
use Nette\Security\Passwords;


/**
 * Users management.
 */
class UserManager extends Nette\Object implements Nette\Security\IAuthenticator
{
	const
		TABLE_NAME = 'membres',
		COLUMN_ID = 'id',
		COLUMN_NAME = 'pseudo', // changed my column since first message (nick->pseudo)
		COLUMN_PASSWORD_HASH = 'password',
		COLUMN_ROLE = 'role';


	/** @var Nette\Database\Context */
	private $database;


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


	/**
	 * Performs an authentication.
	 * @return Nette\Security\Identity
	 * @throws Nette\Security\AuthenticationException
	 */
	public function authenticate(array $credentials)
	{
		list($username, $password) = $credentials;

		$row = $this->database->table(self::TABLE_NAME)->where(self::COLUMN_NAME, $username)->fetch();

		if (!$row) {
			throw new Nette\Security\AuthenticationException('Le pseudo est incorrect.', self::IDENTITY_NOT_FOUND);

		} elseif (!Passwords::verify($password, $row[self::COLUMN_PASSWORD_HASH])) {
			throw new Nette\Security\AuthenticationException('Le mot de passe est incorrect.', self::INVALID_CREDENTIAL);

		} elseif (Passwords::needsRehash($row[self::COLUMN_PASSWORD_HASH])) {
			$row->update(array(
				self::COLUMN_PASSWORD_HASH => Passwords::hash($password),
			));
		}

		$arr = $row->toArray();
		unset($arr[self::COLUMN_PASSWORD_HASH]);
		return new Nette\Security\Identity($row[self::COLUMN_ID], $row[self::COLUMN_ROLE], $arr);
	}


	/**
	 * Adds new user.
	 * @param  string
	 * @param  string
	 * @return void
	 */
	public function add($username, $password)
	{
		try {
			$this->database->table(self::TABLE_NAME)->insert(array(
				self::COLUMN_NAME => $username,
				self::COLUMN_PASSWORD_HASH => Passwords::hash($password),
			));
		} catch (Nette\Database\UniqueConstraintViolationException $e) {
			throw new DuplicateNameException;
		}
	}

}



class DuplicateNameException extends \Exception
{}

Hope I made a good guess…

greeny
Member | 405
+
+1
-

Weird, this looks good. Can you paste whole exception somewhere? Ideally some service like jsfiddle or something what can also preview it (clickable).

Reka
Member | 19
+
0
-

I haven't touched a thing somewhere else (views, controllers, the first lines in the model UserManager are the only files I modify)…

About the exception : when I browse the folder, I find vendor/security/src/security/AuthentificationException.
This file contents :

namespace Nette\Security;

/**
 * Authentication exception.
 */
class AuthenticationException extends \Exception
{
}

And I don't find \Exception anywhere…
Where is it supposed to be?

I will generate another Nette project and test its behaviour + see if its structure is the same.
It was not the case between the first (20/11/2015) and second project (02/12/2015) I created with composer…

Last edited by Reka (2015-12-09 18:50)

greeny
Member | 405
+
0
-

I did not mean the exception file, but the error page one. You can find it in log/**, probably the latest one. You can open it in browser (its HTML), but also copy-paste to some service like jsfiddle. Please do so, we can then more deeply investigate the problem :)

Milo
Nette Core | 1175
+
0
-

@Reka The \Exception is from PHP core.

If you can, upload somewhere the Tracy's red screen as HTML (in browser, Save As…). Check that it does not contain any password or sensitive data.

esorimer
Member | 114
+
0
-

It seems to me that you have put PDO instance into session somewhere (or object with PDO instance reference …). So the problem is not in the code shown here and will not be possible to find out from the tracy “scary red page”.

You should look for a code, where you are playing with session.

greeny
Member | 405
+
0
-

@esorimer not true, since this exception is thrown on data serialization, we can trace where data were passed to session. And since the exception is thrown at line ->login(…), its 99% that he is passing somewhere ActiveRow or something.

esorimer
Member | 114
+
0
-

@greeny because dump($user->login($identifiant, $mdp)); throws exception and $identifiant and $mdp are for 100% strings, I am not very optimistic that we will be able to find where the ActiveRow or something is added to the session.

greeny
Member | 405
+
0
-

@esorimer But this only passes data to Authenticator. You cannot tell anything until we will see Authenticator from @Reka

Reka
Member | 19
+
0
-

greeny wrote:

I did not mean the exception file, but the error page one. You can find it in log/**, probably the latest one. You can open it in browser (its HTML), but also copy-paste to some service like jsfiddle. Please do so, we can then more deeply investigate the problem :)

Ok, ok… Hum.
So. Under /log/** I have 2 files : .htaccess and web.config.
No HTML file in that folder. A good start, is it so?

Here is the code beyond the error page : https://pastee.org/m8c33

It seems that Esorimer is right about sessions, but I don't know where I use sessions… I have only 2 controllers at this stage of the project, and I haven't used that in my controllers or views.
Furthermore, the only model I have is UserManager (see above).
Is it possible that the sessions come from a vendor I have loaded… ?

Thanks again for your perseverance in helping me.

Last edited by Reka (2015-12-09 20:40)

greeny
Member | 405
+
+1
-

That paste helped, thanks.

Error is in your Authenticator on last lane of authenticate method (return new Nette\Security\Identity($row[self::COLUMN_ID], $row[self::COLUMN_ROLE], $arr);)

First and last arguments are ok, but the middle one is not just value. You are using table roles, so in $row[self::COLUMN_ROLE] is ActiveRow. Possible fix is, that you replace $row[self::COLUMN_ROLE] with $row[self::COLUMN_ROLE]->name (instead of name use the name of column in table roles where is name for that role).

<ot> @esorimer it was exactly what I thought </ot>

Reka
Member | 19
+
0
-

Ok, thank you very very much, it works now !!