Изменение прав (credentials) пользователя без релогина используя sfDoctrineGuardPlugin

Symfony framework

Автор: Александр Степанов

5 февр. 2013 г., 10:09:04  788


Регистрируясь в нашей системе пользователь должен подтвердить свой 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.