Symfony2: заставить пользователей завершить регистрацию после входа в систему, если некоторые поля отсутствуют

Я использую HWIOAuthBundle, чтобы позволить пользователю войти в систему с помощью Oauth, я создал собственного поставщика пользователей, который создает пользователя, если он не существует:

public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
    $attr = $response->getResponse();
    switch($response->getResourceOwner()->getName()) {
        case 'google':
            if(!$user = $this->userRepository->findOneByGoogleId($attr['id'])) {
                if(($user = $this->userRepository->findOneByEmail($attr['email'])) && $attr['verified_email']) {
                    $user->setGoogleId($attr['id']);
                    if(!$user->getFirstname()) {
                        $user->setFirstname($attr['given_name']);
                    }
                    if(!$user->getLastname()) {
                        $user->setLastname($attr['family_name']);
                    }
                    $user->setGoogleName($attr['name']);
                }else{
                    $user = new User();
                    $user->setUsername($this->userRepository->createUsernameByEmail($attr['email']));
                    $user->setEmail($attr['email']);
                    $user->setFirstname($attr['given_name']);
                    $user->setLastname($attr['family_name']);
                    $user->setPassword('');
                    $user->setIsActive(true);
                    $user->setGoogleId($attr['id']);
                    $user->setGoogleName($attr['name']);
                    $user->addGroup($this->groupRepository->findOneByRole('ROLE_USER'));
                    $this->entityManager->persist($user);
                }
            }
            break;
        case 'facebook':
            if(!$user = $this->userRepository->findOneByFacebookId($attr['id'])) {
                if(($user = $this->userRepository->findOneByEmail($attr['email'])) && $attr['verified']) {
                    $user->setFacebookId($attr['id']);
                    if(!$user->getFirstname()) {
                        $user->setFirstname($attr['first_name']);
                    }
                    if(!$user->getLastname()) {
                        $user->setLastname($attr['last_name']);
                    }
                    $user->setFacebookUsername($attr['username']);
                }else{
                    $user = new User();
                    $user->setUsername($this->userRepository->createUsernameByEmail($attr['email']));
                    $user->setEmail($attr['email']);
                    $user->setFirstname($attr['first_name']);
                    $user->setLastname($attr['last_name']);
                    $user->setPassword('');
                    $user->setIsActive(true);
                    $user->setFacebookId($attr['id']);
                    $user->setFacebookUsername($attr['username']);
                    $user->addGroup($this->groupRepository->findOneByRole('ROLE_USER'));
                    $this->entityManager->persist($user);
                }
            }
            break;
    }

    $this->entityManager->flush();


    if (null === $user) {
        throw new AccountNotLinkedException(sprintf("User '%s' not found.", $attr['email']));
    }

    return $user;
}

Проблема в том, что твиттер, например, не дает адрес электронной почты, или я хочу, чтобы некоторые дополнительные поля были включены до создания нового пользователя. Есть ли способ перенаправить пользователя на форму «полной регистрации» перед ее созданием?

Я попытался добавить прослушиватель запросов, который при каждом запросе, если пользователь вошел в систему, проверяет, есть ли электронная почта, и если нет, он перенаправляет на страницу complete_registration, но он будет перенаправлять также, если пользователь переходит на домашнюю страницу, выйти из системы или что-то еще, я хочу перенаправить его, только если он попытается получить доступ к некоторым страницам с ограниченным доступом.

Или, лучше, не создавайте его, пока он не предоставит всю необходимую информацию.


person alex88    schedule 06.11.2012    source источник
comment
@elnur что ты редактировал? Просто чтобы знать, если что-то не так   -  person alex88    schedule 06.11.2012
comment
Нажмите на отметку времени последнего редактирования и убедитесь сами.   -  person Elnur Abdurrakhimov    schedule 06.11.2012
comment
Какой грубый ответ, плохое настроение сегодня?   -  person alex88    schedule 07.11.2012
comment
Нет. Я только что научил тебя ловить рыбу вместо того, чтобы дать тебе рыбу. ;)   -  person Elnur Abdurrakhimov    schedule 07.11.2012


Ответы (2)


Я нашел решение сам, я вручную создал новое исключение:

<?php

namespace Acme\UserBundle\Exception;

use Symfony\Component\Security\Core\Exception\AuthenticationException;
use HWI\Bundle\OAuthBundle\Security\Core\Exception\OAuthAwareExceptionInterface;

/**
 * IncompleteUserException is thrown when the user isn't fully registered (e.g.: missing some informations).
 *
 * @author Alessandro Tagliapietra http://www.alexnetwork.it/
 */
class IncompleteUserException extends AuthenticationException implements OAuthAwareExceptionInterface
{
    private $user;
    private $accessToken;
    private $resourceOwnerName;

    /**
     * {@inheritdoc}
     */
    public function setAccessToken($accessToken)
    {
        $this->accessToken = $accessToken;
    }

    /**
     * {@inheritdoc}
     */
    public function getAccessToken()
    {
        return $this->accessToken;
    }

    /**
     * {@inheritdoc}
     */
    public function getResourceOwnerName()
    {
        return $this->resourceOwnerName;
    }

    /**
     * {@inheritdoc}
     */
    public function setResourceOwnerName($resourceOwnerName)
    {
        $this->resourceOwnerName = $resourceOwnerName;
    }

    public function setUser($user)
    {
        $this->user = $user;
    }

    public function getUser($user)
    {
        return $this->user;
    }

    public function serialize()
    {
        return serialize(array(
            $this->user,
            $this->accessToken,
            $this->resourceOwnerName,
            parent::serialize(),
        ));
    }

    public function unserialize($str)
    {
        list(
            $this->user,
            $this->accessToken,
            $this->resourceOwnerName,
            $parentData
        ) = unserialize($str);
        parent::unserialize($parentData);
    }
}

Таким образом, в пользовательском поставщике пользователей Oauth, когда я проверяю, существует ли пользователь, или создаю нового пользователя, я проверяю, отсутствуют ли обязательные поля:

if (!$user->getEmail()) {
    $e = new IncompleteUserException("Your account doesn't has a mail set");
    $e->setUser($user);
    throw $e;
}

В этом случае пользователь будет перенаправлен на форму входа с этим исключением в сеансе, поэтому на странице входа я делаю:

if($error instanceof IncompleteUserException) {
    $session->set(SecurityContext::AUTHENTICATION_ERROR, $error);
    return $this->redirect($this->generateUrl('register_complete'));
}

И он будет перенаправлен на форму с $user в исключении, поэтому он может запросить только недостающую информацию, а затем войти в систему пользователя.

person alex88    schedule 07.11.2012
comment
Это решение не очень элегантно, но оно должно работать. - person mate64; 29.12.2013

Я столкнулся с похожей проблемой при переходе на sf-сайт. После миграции я хотел, чтобы перенесенные пользователи заполнили свой профиль, а вновь зарегистрированные пользователи могли сразу приступить к работе.

Я решил это похоже на вашу идею, используя RequestListener, но добавил белый список страниц, которые разрешены пользователю без заполнения его профиля. В зависимости от количества страниц, к которым вы хотите, чтобы пользователь имел доступ без заполнения своего профиля, вы также можете рассмотреть возможность использования черного списка.

Я не проверял наличие определенного поля при перенаправлении на страницу завершения профиля, но добавил роль «ROLE_MIGRATION» при переносе базы данных пользователей. В вашем случае вы можете добавить роль при создании пользователя через oauth.

Вот код моего прослушивателя запросов:

public function onRequest(GetResponseEvent $evt)
{

    if (HttpKernelInterface::MASTER_REQUEST !== $evt->getRequestType())
    {
        return;
    }

    $token = $this->securityContext->getToken();

    if(!is_object($token)) or not migrating
    {
        // user is not logged in
        return;
    }

    $user = $token->getUser();

    if(!$user instanceof User || !$user->hasRole('ROLE_MIGRATING'))
    {
        // different user class or already migrated
        return;
    }

    $openPaths = array(
        '/start-2.0',
        '/css',
        '/js',
        '/images',
        '/media',
        '/geo',
        '/_wdt',
        '/logout', or not migrating
        '/terms',
        '/contact',
        '/about',
        '/locale/set'
    );

    foreach($openPaths as $p)
    {
        if(strpos($evt->getRequest()->getPathInfo(),$p)===0)
        {
            // path is open for migrating users
            return;
        }
    }



    header('Location: /start-2.0');
    exit;
}
person room13    schedule 06.11.2012
comment
Я думаю, что создание исключения при входе в систему должно быть лучшим способом, я проведу несколько тестов этой ночью и опубликую ответ завтра, если получу что-то хорошее! Потому что ваш подход должен меняться каждый раз, когда вы добавляете несколько общедоступных страниц, и я не просто запускаю миграцию, но спасибо за ответ. - person alex88; 07.11.2012