Изменение прав (credentials) пользователя без релогина используя sfDoctrineGuardPlugin
Регистрируясь в нашей системе пользователь должен подтвердить свой email. Как только пользователь подтверждает свой email его с чистой совестью можно добавить в группу «client» и разрешить использовать все возможности нашей системы не требуя перелогиниться. И вот вчера, решая эту простую задачу я столкнулся с очень любопытным явлением, о котором я бы хотел рассказать. Итак, создадим модуль user, в котором создадим action activate. Приблизительный код будет такой:
findOneByUserName($request->getParameter('email'));
if ($user->activation_code != $request->getParameter('code'))
return false;
// код проверен, добавляем в группу "клиент"
$user->addGroupByName('client');
// Логинимся, не были залогиненны
$this->getUser()->signIn($user);
// Вот главный код: обновляем текущие группы и права
$guard_user->reloadGroupsAndPermissions();
$guard_user->loadGroupsAndPermissions();
$this->redirect('@homepage');
}
}
Из комментариев ясно что если код совпадает то мы добавляем пользователя в группу клиентов и загружаем его новые права. Взглянем детальнее: reloadGroupsAndPermissions — сбрасывает текущие права что бы loadGroupsAndPermissions мог загрузить их из базы. Вполне очевидное но, оказывается, неработающее решение! Однако при повторном вызове этого метода права все же обновляются. Все просто: оптимизируя Doctrine может использовать уже загруженные устаревшие данные из своего кеша вместо обращения к базе. Ставим $user->refresh(true); перед вызовом этих функций и опять ничего: поведение не изменяется. Логичные методы иссякают, варианты исчерпываются и остается только копаться в коде и пытаться дебажить код с помощью echo и print_r. Вот и начинается странность: эмпирически устанавливается что метод refresh должен быть вызван в методе объекта $user класса sfGuardUser а не «снаружи». Как только вместо $user->refresh(true) из executeEmailActivate я написал $this->refresh(true); первой строчкой в getAllPermissions в классе PluginsfGuardUser все заработало как надо: после редиректа кредентиалы устанавливаются и открываются кабинеты клиента системы. Благо что разработчики симфонии позаботились о том что бы можно было вносить изменения в функционал объектов системы (и внешних плагинов) не изменяя файлов. Функционал класса sfGuardUser реализован в файле плагина в классе PluginsfGuardUser, а сам класс находится в файле в папке lib/model/doctrine/sfDoctrineGuardPlugin нашего проекта и доступен для редактирования. В нем мы переопределим функцию getAllPermissions:
refresh(true);
return parent::getAllPermissions();
}
}
Этого достаточно что бы обновить права пользователя «на лету». Но мистика с методом refresh для меня осталась загадкой и наверное задам ее разработчикам Doctrine и Symfony.