mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-24 02:13:15 +00:00
Update to latest auth-providers API
This commit is contained in:
@@ -33,6 +33,9 @@ dev:
|
|||||||
search-engine: phrasea
|
search-engine: phrasea
|
||||||
task-manager: task_manager
|
task-manager: task_manager
|
||||||
authentication:
|
authentication:
|
||||||
|
auto-create:
|
||||||
|
enabled: false
|
||||||
|
templates: []
|
||||||
captcha:
|
captcha:
|
||||||
trials-before-failure: 9
|
trials-before-failure: 9
|
||||||
providers:
|
providers:
|
||||||
@@ -97,6 +100,9 @@ prod:
|
|||||||
search-engine: phrasea
|
search-engine: phrasea
|
||||||
task-manager: task_manager
|
task-manager: task_manager
|
||||||
authentication:
|
authentication:
|
||||||
|
auto-create:
|
||||||
|
enabled: false
|
||||||
|
templates: []
|
||||||
captcha:
|
captcha:
|
||||||
trials-before-failure: 9
|
trials-before-failure: 9
|
||||||
providers:
|
providers:
|
||||||
@@ -161,6 +167,9 @@ test:
|
|||||||
search-engine: phrasea
|
search-engine: phrasea
|
||||||
task-manager: task_manager
|
task-manager: task_manager
|
||||||
authentication:
|
authentication:
|
||||||
|
auto-create:
|
||||||
|
enabled: false
|
||||||
|
templates: []
|
||||||
captcha:
|
captcha:
|
||||||
trials-before-failure: 9
|
trials-before-failure: 9
|
||||||
providers:
|
providers:
|
||||||
|
|||||||
@@ -30,6 +30,16 @@ class AccountCreator
|
|||||||
$this->templates = $templates;
|
$this->templates = $templates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default templates
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getTemplates()
|
||||||
|
{
|
||||||
|
return $this->templates;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Boolean
|
* @return Boolean
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Application as PhraseaApplication;
|
|||||||
use Alchemy\Phrasea\Authentication\Exception\NotAuthenticatedException;
|
use Alchemy\Phrasea\Authentication\Exception\NotAuthenticatedException;
|
||||||
use Alchemy\Phrasea\Authentication\Exception\AuthenticationException;
|
use Alchemy\Phrasea\Authentication\Exception\AuthenticationException;
|
||||||
use Alchemy\Phrasea\Authentication\Context;
|
use Alchemy\Phrasea\Authentication\Context;
|
||||||
|
use Alchemy\Phrasea\Authentication\Provider\ProviderInterface;
|
||||||
use Alchemy\Phrasea\Core\Event\LogoutEvent;
|
use Alchemy\Phrasea\Core\Event\LogoutEvent;
|
||||||
use Alchemy\Phrasea\Core\Event\PreAuthenticate;
|
use Alchemy\Phrasea\Core\Event\PreAuthenticate;
|
||||||
use Alchemy\Phrasea\Core\Event\PostAuthenticate;
|
use Alchemy\Phrasea\Core\Event\PostAuthenticate;
|
||||||
@@ -30,10 +31,10 @@ use Alchemy\Phrasea\Notification\Mail\MailSuccessEmailConfirmationUnregistered;
|
|||||||
use Alchemy\Phrasea\Authentication\Exception\RequireCaptchaException;
|
use Alchemy\Phrasea\Authentication\Exception\RequireCaptchaException;
|
||||||
use Alchemy\Phrasea\Authentication\Exception\AccountLockedException;
|
use Alchemy\Phrasea\Authentication\Exception\AccountLockedException;
|
||||||
use Alchemy\Phrasea\Form\Login\PhraseaAuthenticationForm;
|
use Alchemy\Phrasea\Form\Login\PhraseaAuthenticationForm;
|
||||||
use Alchemy\Phrasea\Form\Login\PhraseaAuthenticationWithMappingForm;
|
|
||||||
use Alchemy\Phrasea\Form\Login\PhraseaForgotPasswordForm;
|
use Alchemy\Phrasea\Form\Login\PhraseaForgotPasswordForm;
|
||||||
use Alchemy\Phrasea\Form\Login\PhraseaRecoverPasswordForm;
|
use Alchemy\Phrasea\Form\Login\PhraseaRecoverPasswordForm;
|
||||||
use Alchemy\Phrasea\Form\Login\PhraseaRegisterForm;
|
use Alchemy\Phrasea\Form\Login\PhraseaRegisterForm;
|
||||||
|
use Doctrine\ORM\EntityManager;
|
||||||
use Entities\UsrAuthProvider;
|
use Entities\UsrAuthProvider;
|
||||||
use Silex\Application;
|
use Silex\Application;
|
||||||
use Silex\ControllerProviderInterface;
|
use Silex\ControllerProviderInterface;
|
||||||
@@ -112,30 +113,6 @@ class Login implements ControllerProviderInterface
|
|||||||
$app['firewall']->requireNotAuthenticated();
|
$app['firewall']->requireNotAuthenticated();
|
||||||
})->bind('login_authentication_provider_callback');
|
})->bind('login_authentication_provider_callback');
|
||||||
|
|
||||||
// Displays a form to add a mapping from an AuthProvider identity to a Phraseanet user that has been found
|
|
||||||
$controllers->get('/provider/{providerId}/add-mapping/', 'login.controller:authenticationMapping')
|
|
||||||
->before(function(Request $request) use ($app) {
|
|
||||||
$app['firewall']->requireNotAuthenticated();
|
|
||||||
})->bind('login_authentication_provider_mapping');
|
|
||||||
|
|
||||||
// Submit the mapping from an AuthProvider identity to a Phraseanet user that has been found
|
|
||||||
$controllers->post('/provider/{providerId}/add-mapping/', 'login.controller:authenticationDoMapToAccount')
|
|
||||||
->before(function(Request $request) use ($app) {
|
|
||||||
$app['firewall']->requireNotAuthenticated();
|
|
||||||
})->bind('login_authentication_provider_do_mapping');
|
|
||||||
|
|
||||||
// Displays a form to bind an AuthProvider identity to an account or create one
|
|
||||||
$controllers->get('/provider/{providerId}/bind-account/', 'login.controller:authenticationBindToAccount')
|
|
||||||
->before(function(Request $request) use ($app) {
|
|
||||||
$app['firewall']->requireNotAuthenticated();
|
|
||||||
})->bind('login_authentication_provider_bind');
|
|
||||||
|
|
||||||
// Submits the form to bind an AuthProvider identity to an account or create one
|
|
||||||
$controllers->post('/provider/{providerId}/bind-account/', 'login.controller:authenticationDoBindToAccount')
|
|
||||||
->before(function(Request $request) use ($app) {
|
|
||||||
$app['firewall']->requireNotAuthenticated();
|
|
||||||
})->bind('login_authentication_provider_do_bind');
|
|
||||||
|
|
||||||
// Logout end point
|
// Logout end point
|
||||||
$controllers->get('/logout/', 'login.controller:logout')
|
$controllers->get('/logout/', 'login.controller:logout')
|
||||||
->before(function(Request $request) use ($app) {
|
->before(function(Request $request) use ($app) {
|
||||||
@@ -150,6 +127,9 @@ class Login implements ControllerProviderInterface
|
|||||||
|
|
||||||
// Classic registration end point
|
// Classic registration end point
|
||||||
$controllers->match('/register-classic/', 'login.controller:doRegistration')
|
$controllers->match('/register-classic/', 'login.controller:doRegistration')
|
||||||
|
->before(function(Request $request) use ($app) {
|
||||||
|
$app['firewall']->requireNotAuthenticated();
|
||||||
|
})
|
||||||
->bind('login_register_classic');
|
->bind('login_register_classic');
|
||||||
|
|
||||||
// Provide a JSON serialization of registration fields configuration
|
// Provide a JSON serialization of registration fields configuration
|
||||||
@@ -157,12 +137,6 @@ class Login implements ControllerProviderInterface
|
|||||||
return $app->json($app['registration.fields']);
|
return $app->json($app['registration.fields']);
|
||||||
})->bind('login_registration_fields');
|
})->bind('login_registration_fields');
|
||||||
|
|
||||||
// A TESTER
|
|
||||||
// Registers with AuthProviders
|
|
||||||
$controllers->get('/register-provider/', function(PhraseaApplication $app, Request $request) {
|
|
||||||
return $app['twig']->render('login/register-provider.html.twig');
|
|
||||||
})->bind('login_register_provider');
|
|
||||||
|
|
||||||
// Unlocks an email address that is currently locked
|
// Unlocks an email address that is currently locked
|
||||||
$controllers->get('/register-confirm/', 'login.controller:registerConfirm')
|
$controllers->get('/register-confirm/', 'login.controller:registerConfirm')
|
||||||
->before(function(Request $request) use ($app) {
|
->before(function(Request $request) use ($app) {
|
||||||
@@ -197,6 +171,10 @@ class Login implements ControllerProviderInterface
|
|||||||
|
|
||||||
public function doRegistration(PhraseaApplication $app, Request $request)
|
public function doRegistration(PhraseaApplication $app, Request $request)
|
||||||
{
|
{
|
||||||
|
if (!$app['registration.enabled']) {
|
||||||
|
$app->abort(404, 'Registration is disabled');
|
||||||
|
}
|
||||||
|
|
||||||
$form = $app->form(new PhraseaRegisterForm(
|
$form = $app->form(new PhraseaRegisterForm(
|
||||||
$app, $app['registration.optional-fields'], $app['registration.fields']
|
$app, $app['registration.optional-fields'], $app['registration.fields']
|
||||||
));
|
));
|
||||||
@@ -205,6 +183,35 @@ class Login implements ControllerProviderInterface
|
|||||||
$form->bind($request);
|
$form->bind($request);
|
||||||
$data = $form->getData();
|
$data = $form->getData();
|
||||||
|
|
||||||
|
$provider = null;
|
||||||
|
if ($data['provider-id']) {
|
||||||
|
try {
|
||||||
|
$provider = $this->findProvider($app, $data['provider-id']);
|
||||||
|
} catch (NotFoundHttpException $e) {
|
||||||
|
$app->addFlash('error', _('You tried to register with an unknown provider'));
|
||||||
|
|
||||||
|
return $app->redirect($app->path('login_register'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$token = $provider->getToken();
|
||||||
|
} catch (NotAuthenticatedException $e) {
|
||||||
|
$app->addFlash('error', _('You tried to register with an unknown provider'));
|
||||||
|
|
||||||
|
return $app->redirect($app->path('login_register'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$userAuthProvider = $app['EM']
|
||||||
|
->getRepository('Entities\UsrAuthProvider')
|
||||||
|
->findWithProviderAndId($token->getProvider()->getId(), $token->getId());
|
||||||
|
|
||||||
|
if (null !== $userAuthProvider) {
|
||||||
|
$this->postAuthProcess($app, $userAuthProvider->getUser($app));
|
||||||
|
|
||||||
|
return $app->redirect($request->query->get('redirect', $app->path('prod')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ($form->isValid()) {
|
if ($form->isValid()) {
|
||||||
if ($data['password'] !== $data['passwordConfirm']) {
|
if ($data['password'] !== $data['passwordConfirm']) {
|
||||||
@@ -267,6 +274,11 @@ class Login implements ControllerProviderInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (null !== $provider) {
|
||||||
|
$this->attachProviderToUser($app['EM'], $provider, $user);
|
||||||
|
$app['EM']->flush();
|
||||||
|
}
|
||||||
|
|
||||||
$demandOK = array();
|
$demandOK = array();
|
||||||
|
|
||||||
if ($app['phraseanet.registry']->get('GV_autoregister')) {
|
if ($app['phraseanet.registry']->get('GV_autoregister')) {
|
||||||
@@ -321,6 +333,17 @@ class Login implements ControllerProviderInterface
|
|||||||
} catch (FormProcessingException $e) {
|
} catch (FormProcessingException $e) {
|
||||||
$app->addFlash('error', $e->getMessage());
|
$app->addFlash('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
} elseif (null !== $request->query->get('providerId')) {
|
||||||
|
$provider = $this->findProvider($app, $request->query->get('providerId'));
|
||||||
|
$identity = $provider->getIdentity();
|
||||||
|
|
||||||
|
$form->bind(array_filter(array(
|
||||||
|
'email' => $identity->getEmail(),
|
||||||
|
'firstname' => $identity->getFirstname(),
|
||||||
|
'lastname' => $identity->getLastname(),
|
||||||
|
'company' => $identity->getCompany(),
|
||||||
|
'provider-id' => $provider->getId(),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $app['twig']->render('login/register-classic.html.twig', array(
|
return $app['twig']->render('login/register-classic.html.twig', array(
|
||||||
@@ -331,6 +354,22 @@ class Login implements ControllerProviderInterface
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function attachProviderToUser(EntityManager $em, ProviderInterface $provider, \User_Adapter $user)
|
||||||
|
{
|
||||||
|
$usrAuthProvider = new UsrAuthProvider();
|
||||||
|
$usrAuthProvider->setDistantId($provider->getToken()->getId());
|
||||||
|
$usrAuthProvider->setProvider($provider->getId());
|
||||||
|
$usrAuthProvider->setUsrId($user->get_id());
|
||||||
|
|
||||||
|
try {
|
||||||
|
$provider->logout();
|
||||||
|
} catch (RuntimeException $e) {
|
||||||
|
// log these errors
|
||||||
|
}
|
||||||
|
|
||||||
|
$em->persist($usrAuthProvider);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a confirmation mail after register
|
* Send a confirmation mail after register
|
||||||
*
|
*
|
||||||
@@ -553,8 +592,8 @@ class Login implements ControllerProviderInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $app['twig']->render('login/forgot-password.html.twig', array(
|
return $app['twig']->render('login/forgot-password.html.twig', array(
|
||||||
'login' => new \login(),
|
'login' => new \login(),
|
||||||
'form' => $form->createView(),
|
'form' => $form->createView(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,8 +606,11 @@ class Login implements ControllerProviderInterface
|
|||||||
*/
|
*/
|
||||||
public function displayRegisterForm(PhraseaApplication $app, Request $request)
|
public function displayRegisterForm(PhraseaApplication $app, Request $request)
|
||||||
{
|
{
|
||||||
|
if (!$app['registration.enabled']) {
|
||||||
|
$app->abort(404, 'Registration is disabled');
|
||||||
|
}
|
||||||
|
|
||||||
if (0 < count($app['authentication.providers'])) {
|
if (0 < count($app['authentication.providers'])) {
|
||||||
//neutron a verifier
|
|
||||||
return $app['twig']->render('login/register.html.twig', array(
|
return $app['twig']->render('login/register.html.twig', array(
|
||||||
'login' => new \login(),
|
'login' => new \login(),
|
||||||
));
|
));
|
||||||
@@ -777,6 +819,7 @@ class Login implements ControllerProviderInterface
|
|||||||
|
|
||||||
public function authenticationCallback(PhraseaApplication $app, Request $request, $providerId)
|
public function authenticationCallback(PhraseaApplication $app, Request $request, $providerId)
|
||||||
{
|
{
|
||||||
|
$login = new \login();
|
||||||
$provider = $this->findProvider($app, $providerId);
|
$provider = $this->findProvider($app, $providerId);
|
||||||
|
|
||||||
// triggers what's necessary
|
// triggers what's necessary
|
||||||
@@ -789,83 +832,58 @@ class Login implements ControllerProviderInterface
|
|||||||
return $app->redirect($app->path('homepage'));
|
return $app->redirect($app->path('homepage'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's find a match
|
|
||||||
$userAuthProvider = $app['EM']
|
$userAuthProvider = $app['EM']
|
||||||
->getRepository('Entities\UsrAuthProvider')
|
->getRepository('Entities\UsrAuthProvider')
|
||||||
->findOneBy(array(
|
->findWithProviderAndId($token->getProvider()->getId(), $token->getId());
|
||||||
'provider' => $token->getProvider()->getId(),
|
|
||||||
'distant_id' => $token->getId(),
|
|
||||||
));
|
|
||||||
|
|
||||||
if ($userAuthProvider) {
|
if (null !== $userAuthProvider) {
|
||||||
$this->postAuthProcess($app, $userAuthProvider->getUser($app));
|
$this->postAuthProcess($app, $userAuthProvider->getUser($app));
|
||||||
$target = $request->query->get('redirect', $app->path('prod'));
|
|
||||||
|
|
||||||
return $app->redirect($target);
|
return $app->redirect($request->query->get('redirect', $app->path('prod')));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ($app['authentication.suggestion-finder']->find($token)) {
|
$user = $app['authentication.suggestion-finder']->find($token);
|
||||||
return $app->redirect($app['url_generator']->generate('login_authentication_provider_mapping', array(
|
|
||||||
'providerId' => $providerId,
|
|
||||||
'id' => $token->getId(),
|
|
||||||
)));
|
|
||||||
} else {
|
|
||||||
return $app->redirect($app['url_generator']->generate('login_authentication_provider_bind', array(
|
|
||||||
'providerId' => $providerId,
|
|
||||||
'id' => $token->getId(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
} catch (NotAuthenticatedException $e) {
|
|
||||||
$app->addFlash('error', _('Unable to retrieve provider identity'));
|
|
||||||
|
|
||||||
return $app->redirect($app->path('homepage'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function authenticationMapping(PhraseaApplication $app, Request $request, $providerId)
|
|
||||||
{
|
|
||||||
$provider = $this->findProvider($app, $providerId);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$token = $provider->getToken();
|
|
||||||
$suggestion = $app['authentication.suggestion-finder']->find($token);
|
|
||||||
} catch (NotAuthenticatedException $e) {
|
} catch (NotAuthenticatedException $e) {
|
||||||
$app->addFlash('error', _('Unable to retrieve provider identity'));
|
$app->addFlash('error', _('Unable to retrieve provider identity'));
|
||||||
|
|
||||||
return $app->redirect($app->path('homepage'));
|
return $app->redirect($app->path('homepage'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$form = $app->form(new PhraseaAuthenticationWithMappingForm(), array(
|
if (null !== $user) {
|
||||||
'login' => $suggestion->get_login(),
|
$this->attachProviderToUser($app['EM'], $provider, $user);
|
||||||
));
|
$app['EM']->flush();
|
||||||
|
|
||||||
return $app['twig']->render('login/providers/mapping.html.twig', array(
|
$this->postAuthProcess($app, $user);
|
||||||
'provider' => $provider,
|
|
||||||
'recaptcha_display' => $app->isCaptchaRequired(),
|
return $app->redirect($request->query->get('redirect', $app->path('prod')));
|
||||||
'login' => new \login(),
|
}
|
||||||
'form' => $form->createView(),
|
|
||||||
'token' => $token,
|
if ($app['authentication.providers.account-creator']->isEnabled()) {
|
||||||
'suggestion' => $suggestion,
|
$user = $app['authentication.providers.account-creator']->create($app, $token->getId(), $token->getIdentity()->getEmail(), $token->getTemplates());
|
||||||
));
|
|
||||||
}
|
$this->attachProviderToUser($app['EM'], $provider, $user);
|
||||||
|
$app['EM']->flush();
|
||||||
public function authenticationBindToAccount(PhraseaApplication $app, Request $request, $providerId)
|
|
||||||
{
|
$this->postAuthProcess($app, $user);
|
||||||
$provider = $this->findProvider($app, $providerId);
|
|
||||||
|
return $app->redirect($request->query->get('redirect', $app->path('prod')));
|
||||||
$form = $app->form(new PhraseaAuthenticationForm(), array(
|
} elseif ($app['registration.enabled']) {
|
||||||
));
|
return $app->redirect($app->path('login_register_classic', array('providerId' => $providerId)));
|
||||||
|
}
|
||||||
return $app['twig']->render('login/providers/bind.html.twig', array(
|
|
||||||
'login' => new \login(),
|
$app->addFlash('error', _('Your identity is not recognized.'));
|
||||||
'recaptcha_display' => $app->isCaptchaRequired(),
|
|
||||||
'provider' => $provider,
|
return $app->redirect($app->path('homepage'));
|
||||||
'form' => $form->createView(),
|
|
||||||
'token' => $provider->getToken(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param PhraseaApplication $app
|
||||||
|
* @param string $providerId
|
||||||
|
* @return ProviderInterface
|
||||||
|
* @throws NotFoundHttpException
|
||||||
|
*/
|
||||||
private function findProvider(PhraseaApplication $app, $providerId)
|
private function findProvider(PhraseaApplication $app, $providerId)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -875,80 +893,6 @@ class Login implements ControllerProviderInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authenticationDoMapToAccount(PhraseaApplication $app, Request $request, $providerId)
|
|
||||||
{
|
|
||||||
$provider = $this->findProvider($app, $providerId);
|
|
||||||
|
|
||||||
$form = $app->form(new PhraseaAuthenticationWithMappingForm());
|
|
||||||
|
|
||||||
$redirector = function (array $params = array()) use ($app, $providerId) {
|
|
||||||
$params = array_merge($params, array(
|
|
||||||
'providerId' => $providerId,
|
|
||||||
));
|
|
||||||
|
|
||||||
return $app->redirect($app->path('login_authentication_provider_mapping', $params));
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
$response = $this->doAuthentication($app, $request, $form, $redirector);
|
|
||||||
} catch (AuthenticationException $e) {
|
|
||||||
return $e->getResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
$usrAuthProvider = new UsrAuthProvider();
|
|
||||||
$usrAuthProvider->setDistantId($provider->getToken()->getId());
|
|
||||||
$usrAuthProvider->setProvider($provider->getId());
|
|
||||||
$usrAuthProvider->setUsrId($app['authentication']->getUser()->get_id());
|
|
||||||
|
|
||||||
try {
|
|
||||||
$provider->logout();
|
|
||||||
} catch (RuntimeException $e) {
|
|
||||||
// log these errors
|
|
||||||
}
|
|
||||||
|
|
||||||
$app['EM']->persist($usrAuthProvider);
|
|
||||||
$app['EM']->flush();
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function authenticationDoBindToAccount(PhraseaApplication $app, Request $request, $providerId)
|
|
||||||
{
|
|
||||||
$provider = $this->findProvider($app, $providerId);
|
|
||||||
|
|
||||||
$form = $app->form(new PhraseaAuthenticationForm());
|
|
||||||
|
|
||||||
$redirector = function (array $params = array()) use ($app, $providerId) {
|
|
||||||
$params = array_merge($params, array(
|
|
||||||
'providerId' => $providerId,
|
|
||||||
));
|
|
||||||
|
|
||||||
return $app->redirect($app->path('login_authentication_provider_mapping', $params));
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
$response = $this->doAuthentication($app, $request, $form, $redirector);
|
|
||||||
} catch (AuthenticationException $e) {
|
|
||||||
return $e->getResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
$usrAuthProvider = new UsrAuthProvider();
|
|
||||||
$usrAuthProvider->setDistantId($provider->getToken()->getId());
|
|
||||||
$usrAuthProvider->setProvider($provider->getId());
|
|
||||||
$usrAuthProvider->setUsrId($app['authentication']->getUser()->get_id());
|
|
||||||
|
|
||||||
$app['EM']->persist($usrAuthProvider);
|
|
||||||
$app['EM']->flush();
|
|
||||||
|
|
||||||
try {
|
|
||||||
$provider->logout();
|
|
||||||
} catch (RuntimeException $e) {
|
|
||||||
// log these errors
|
|
||||||
}
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function doAuthentication(PhraseaApplication $app, Request $request, FormInterface $form, $redirector)
|
private function doAuthentication(PhraseaApplication $app, Request $request, FormInterface $form, $redirector)
|
||||||
{
|
{
|
||||||
if (!is_callable($redirector)) {
|
if (!is_callable($redirector)) {
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Phraseanet
|
|
||||||
*
|
|
||||||
* (c) 2005-2013 Alchemy
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\Phrasea\Form\Login;
|
|
||||||
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
|
||||||
|
|
||||||
class PhraseaAuthenticationWithMappingForm extends PhraseaAuthenticationForm
|
|
||||||
{
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
|
||||||
{
|
|
||||||
parent::buildForm($builder, $options);
|
|
||||||
|
|
||||||
$builder->add('login', 'hidden', array(
|
|
||||||
'required' => true,
|
|
||||||
'disabled' => $options['disabled'],
|
|
||||||
'constraints' => array(
|
|
||||||
new Assert\NotBlank(),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getName()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -71,6 +71,8 @@ class PhraseaRegisterForm extends AbstractType
|
|||||||
))),
|
))),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$builder->add('provider-id', 'hidden');
|
||||||
|
|
||||||
require_once($this->app['phraseanet.registry']->get('GV_RootPath') . 'lib/classes/deprecated/inscript.api.php');
|
require_once($this->app['phraseanet.registry']->get('GV_RootPath') . 'lib/classes/deprecated/inscript.api.php');
|
||||||
$baseIds = array();
|
$baseIds = array();
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ dev:
|
|||||||
search-engine: phrasea
|
search-engine: phrasea
|
||||||
task-manager: task_manager
|
task-manager: task_manager
|
||||||
authentication:
|
authentication:
|
||||||
|
auto-create:
|
||||||
|
enabled: false
|
||||||
|
templates: []
|
||||||
captcha:
|
captcha:
|
||||||
trials-before-failure: 9
|
trials-before-failure: 9
|
||||||
providers:
|
providers:
|
||||||
@@ -97,6 +100,9 @@ prod:
|
|||||||
search-engine: phrasea
|
search-engine: phrasea
|
||||||
task-manager: task_manager
|
task-manager: task_manager
|
||||||
authentication:
|
authentication:
|
||||||
|
auto-create:
|
||||||
|
enabled: false
|
||||||
|
templates: []
|
||||||
captcha:
|
captcha:
|
||||||
trials-before-failure: 9
|
trials-before-failure: 9
|
||||||
providers:
|
providers:
|
||||||
@@ -161,6 +167,9 @@ test:
|
|||||||
search-engine: phrasea
|
search-engine: phrasea
|
||||||
task-manager: task_manager
|
task-manager: task_manager
|
||||||
authentication:
|
authentication:
|
||||||
|
auto-create:
|
||||||
|
enabled: false
|
||||||
|
templates: []
|
||||||
captcha:
|
captcha:
|
||||||
trials-before-failure: 9
|
trials-before-failure: 9
|
||||||
providers:
|
providers:
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ class AccountCreatorTest extends \PhraseanetPHPUnitAbstract
|
|||||||
|
|
||||||
$this->assertInstanceOf('User_Adapter', $user);
|
$this->assertInstanceOf('User_Adapter', $user);
|
||||||
$user->delete();
|
$user->delete();
|
||||||
|
$template1->delete();
|
||||||
|
$template2->delete();
|
||||||
|
$template3->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCreateWithAlreadyExistingLogin()
|
public function testCreateWithAlreadyExistingLogin()
|
||||||
|
|||||||
@@ -433,6 +433,59 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
|||||||
$this->assertAngularFlashMessage($crawler, $type, 1, $message);
|
$this->assertAngularFlashMessage($crawler, $type, 1, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetRegisterWithRegisterIdBindDataToForm()
|
||||||
|
{
|
||||||
|
self::$DI['app']['authentication']->closeAccount();
|
||||||
|
|
||||||
|
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
||||||
|
|
||||||
|
self::$DI['app']['authentication.providers'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\ProvidersCollection')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
self::$DI['app']['authentication.providers']->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with($this->equalTo('provider-test'))
|
||||||
|
->will($this->returnValue($provider));
|
||||||
|
|
||||||
|
$identity = $this->getMockBuilder('Alchemy\Phrasea\Authentication\Provider\Token\Identity')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$provider->expects($this->once())
|
||||||
|
->method('getIdentity')
|
||||||
|
->will($this->returnValue($identity));
|
||||||
|
|
||||||
|
$identity->expects($this->once())
|
||||||
|
->method('getEmail')
|
||||||
|
->will($this->returnValue('supermail@superprovider.com'));
|
||||||
|
|
||||||
|
$crawler = self::$DI['client']->request('GET', '/login/register-classic/?providerId=provider-test');
|
||||||
|
|
||||||
|
$this->assertEquals(200, self::$DI['client']->getResponse()->getStatusCode());
|
||||||
|
$this->assertEquals(1, $crawler->filterXPath("//input[@value='supermail@superprovider.com' and @name='email']")->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideRegistrationRouteAndMethods()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('GET', '/login/register/'),
|
||||||
|
array('GET', '/login/register-classic/'),
|
||||||
|
array('POST', '/login/register-classic/'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideRegistrationRouteAndMethods
|
||||||
|
*/
|
||||||
|
public function testGetPostRegisterWhenRegistrationDisabled($method, $route)
|
||||||
|
{
|
||||||
|
self::$DI['app']['registration.enabled'] = false;
|
||||||
|
self::$DI['app']['authentication']->closeAccount();
|
||||||
|
self::$DI['client']->request($method, $route);
|
||||||
|
$this->assertEquals(404, self::$DI['client']->getResponse()->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider provideInvalidRegistrationData
|
* @dataProvider provideInvalidRegistrationData
|
||||||
*/
|
*/
|
||||||
@@ -550,14 +603,14 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
|||||||
public function provideRegistrationData()
|
public function provideRegistrationData()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
array(array(//required field missing
|
array(array(
|
||||||
"password" => 'password',
|
"password" => 'password',
|
||||||
"passwordConfirm" => 'password',
|
"passwordConfirm" => 'password',
|
||||||
"email" => $this->generateEmail(),
|
"email" => $this->generateEmail(),
|
||||||
"accept-tou" => '1',
|
"accept-tou" => '1',
|
||||||
"collections" => null,
|
"collections" => null,
|
||||||
), array()),
|
), array()),
|
||||||
array(array(//extra-field is not required
|
array(array(
|
||||||
"password" => 'password',
|
"password" => 'password',
|
||||||
"passwordConfirm" => 'password',
|
"passwordConfirm" => 'password',
|
||||||
"email" => $this->generateEmail(),
|
"email" => $this->generateEmail(),
|
||||||
@@ -696,6 +749,191 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPostRegisterWithProviderIdAndAlreadyBoundProvider()
|
||||||
|
{
|
||||||
|
self::$DI['app']['registration.fields'] = array();
|
||||||
|
self::$DI['app']['authentication']->closeAccount();
|
||||||
|
|
||||||
|
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
||||||
|
$this->addProvider('provider-test', $provider);
|
||||||
|
|
||||||
|
$entity = $this->getMock('Entities\UsrAuthProvider');
|
||||||
|
$entity->expects($this->any())
|
||||||
|
->method('getUser')
|
||||||
|
->with(self::$DI['app'])
|
||||||
|
->will($this->returnValue(self::$DI['user']));
|
||||||
|
|
||||||
|
$token = new Token($provider, 42);
|
||||||
|
$this->addUsrAuthDoctrineEntitySupport(42, $entity, true);
|
||||||
|
|
||||||
|
$provider->expects($this->once())
|
||||||
|
->method('getToken')
|
||||||
|
->will($this->returnValue($token));
|
||||||
|
|
||||||
|
$parameters = array_merge(array('_token' => 'token'), array(
|
||||||
|
"password" => 'password',
|
||||||
|
"passwordConfirm" => 'password',
|
||||||
|
"email" => $this->generateEmail(),
|
||||||
|
"accept-tou" => '1',
|
||||||
|
"collections" => null,
|
||||||
|
"provider-id" => 'provider-test',
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($parameters as $key => $parameter) {
|
||||||
|
if ('collections' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$demands;
|
||||||
|
}
|
||||||
|
if ('login' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$login;
|
||||||
|
}
|
||||||
|
if ('email' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$DI['client']->request('POST', '/login/register-classic/', $parameters);
|
||||||
|
|
||||||
|
$this->assertTrue(self::$DI['client']->getResponse()->isRedirect());
|
||||||
|
$this->assertEquals('/prod/', self::$DI['client']->getResponse()->headers->get('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostRegisterWithUnknownProvider()
|
||||||
|
{
|
||||||
|
self::$DI['app']['registration.fields'] = array();
|
||||||
|
self::$DI['app']['authentication']->closeAccount();
|
||||||
|
|
||||||
|
$parameters = array_merge(array('_token' => 'token'), array(
|
||||||
|
"password" => 'password',
|
||||||
|
"passwordConfirm" => 'password',
|
||||||
|
"email" => $this->generateEmail(),
|
||||||
|
"accept-tou" => '1',
|
||||||
|
"collections" => null,
|
||||||
|
"provider-id" => 'provider-test',
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($parameters as $key => $parameter) {
|
||||||
|
if ('collections' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$demands;
|
||||||
|
}
|
||||||
|
if ('login' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$login;
|
||||||
|
}
|
||||||
|
if ('email' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$DI['client']->request('POST', '/login/register-classic/', $parameters);
|
||||||
|
|
||||||
|
$this->assertTrue(self::$DI['client']->getResponse()->isRedirect());
|
||||||
|
$this->assertFlashMessagePopulated(self::$DI['app'], 'error', 1);
|
||||||
|
$this->assertEquals('/login/register/', self::$DI['client']->getResponse()->headers->get('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostRegisterWithProviderNotAuthenticated()
|
||||||
|
{
|
||||||
|
self::$DI['app']['registration.fields'] = array();
|
||||||
|
self::$DI['app']['authentication']->closeAccount();
|
||||||
|
|
||||||
|
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
||||||
|
$this->addProvider('provider-test', $provider);
|
||||||
|
|
||||||
|
$provider->expects($this->any())
|
||||||
|
->method('getToken')
|
||||||
|
->will($this->throwException(new NotAuthenticatedException('Not authenticated')));
|
||||||
|
|
||||||
|
$parameters = array_merge(array('_token' => 'token'), array(
|
||||||
|
"password" => 'password',
|
||||||
|
"passwordConfirm" => 'password',
|
||||||
|
"email" => $this->generateEmail(),
|
||||||
|
"accept-tou" => '1',
|
||||||
|
"collections" => null,
|
||||||
|
"provider-id" => 'provider-test',
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($parameters as $key => $parameter) {
|
||||||
|
if ('collections' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$demands;
|
||||||
|
}
|
||||||
|
if ('login' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$login;
|
||||||
|
}
|
||||||
|
if ('email' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$DI['client']->request('POST', '/login/register-classic/', $parameters);
|
||||||
|
|
||||||
|
$this->assertTrue(self::$DI['client']->getResponse()->isRedirect());
|
||||||
|
$this->assertFlashMessagePopulated(self::$DI['app'], 'error', 1);
|
||||||
|
$this->assertEquals('/login/register/', self::$DI['client']->getResponse()->headers->get('location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostRegisterWithProviderId()
|
||||||
|
{
|
||||||
|
self::$DI['app']['registration.fields'] = array();
|
||||||
|
self::$DI['app']['authentication']->closeAccount();
|
||||||
|
|
||||||
|
$emails = array(
|
||||||
|
'Alchemy\Phrasea\Notification\Mail\MailRequestEmailConfirmation'=>0,
|
||||||
|
'Alchemy\Phrasea\Notification\Mail\MailInfoUserRegistered'=>0,
|
||||||
|
'Alchemy\Phrasea\Notification\Mail\MailInfoSomebodyAutoregistered'=>0,
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->mockNotificationsDeliverer($emails);
|
||||||
|
|
||||||
|
$parameters = array_merge(array('_token' => 'token'), array(
|
||||||
|
"password" => 'password',
|
||||||
|
"passwordConfirm" => 'password',
|
||||||
|
"email" => $this->generateEmail(),
|
||||||
|
"accept-tou" => '1',
|
||||||
|
"collections" => null,
|
||||||
|
"provider-id" => 'provider-test',
|
||||||
|
));
|
||||||
|
|
||||||
|
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
||||||
|
$this->addProvider('provider-test', $provider);
|
||||||
|
|
||||||
|
$token = new Token($provider, 42);
|
||||||
|
|
||||||
|
$provider->expects($this->any())
|
||||||
|
->method('getToken')
|
||||||
|
->will($this->returnValue($token));
|
||||||
|
|
||||||
|
foreach ($parameters as $key => $parameter) {
|
||||||
|
if ('collections' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$demands;
|
||||||
|
}
|
||||||
|
if ('login' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$login;
|
||||||
|
}
|
||||||
|
if ('email' === $key && null === $parameter) {
|
||||||
|
$parameters[$key] = self::$email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$DI['client']->request('POST', '/login/register-classic/', $parameters);
|
||||||
|
|
||||||
|
if (false === $userId = \User_Adapter::get_usr_id_from_email(self::$DI['app'], $parameters['email'])) {
|
||||||
|
$this->fail('User not created');
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = new \User_Adapter((int) $userId, self::$DI['app']);
|
||||||
|
|
||||||
|
$ret = self::$DI['app']['EM']->getRepository('\Entities\UsrAuthProvider')
|
||||||
|
->findBy(array('usr_id' => $userId, 'provider' => 'provider-test'));
|
||||||
|
$this->assertCount(1, $ret);
|
||||||
|
|
||||||
|
$user->delete();
|
||||||
|
|
||||||
|
$this->assertGreaterThan(0, $emails['Alchemy\Phrasea\Notification\Mail\MailInfoUserRegistered']);
|
||||||
|
$this->assertEquals(1, $emails['Alchemy\Phrasea\Notification\Mail\MailRequestEmailConfirmation']);
|
||||||
|
$this->assertTrue(self::$DI['client']->getResponse()->isRedirect());
|
||||||
|
$this->assertFlashMessagePopulated(self::$DI['app'], 'info', 1);
|
||||||
|
$this->assertEquals('/login/', self::$DI['client']->getResponse()->headers->get('location'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider provideRegistrationData
|
* @dataProvider provideRegistrationData
|
||||||
*/
|
*/
|
||||||
@@ -1103,10 +1341,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
|||||||
return array(
|
return array(
|
||||||
array('GET', '/login/provider/provider-test/authenticate/'),
|
array('GET', '/login/provider/provider-test/authenticate/'),
|
||||||
array('GET', '/login/provider/provider-test/callback/'),
|
array('GET', '/login/provider/provider-test/callback/'),
|
||||||
array('GET', '/login/provider/provider-test/add-mapping/'),
|
array('GET', '/login/register-classic/?providerId=provider-test'),
|
||||||
array('POST', '/login/provider/provider-test/add-mapping/'),
|
|
||||||
array('GET', '/login/provider/provider-test/bind-account/'),
|
|
||||||
array('POST', '/login/provider/provider-test/bind-account/'),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1173,7 +1408,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
|||||||
->method('onCallback');
|
->method('onCallback');
|
||||||
|
|
||||||
$entity = $this->getMock('Entities\UsrAuthProvider');
|
$entity = $this->getMock('Entities\UsrAuthProvider');
|
||||||
$entity->expects($this->once())
|
$entity->expects($this->any())
|
||||||
->method('getUser')
|
->method('getUser')
|
||||||
->with(self::$DI['app'])
|
->with(self::$DI['app'])
|
||||||
->will($this->returnValue(self::$DI['user']));
|
->will($this->returnValue(self::$DI['user']));
|
||||||
@@ -1194,7 +1429,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
|||||||
$this->assertTrue(self::$DI['app']['authentication']->isAuthenticated());
|
$this->assertTrue(self::$DI['app']['authentication']->isAuthenticated());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAuthenticateProviderCallbackWithSuggestion()
|
public function testAuthenticateProviderCallbackWithSuggestionBindProviderToUser()
|
||||||
{
|
{
|
||||||
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
||||||
$this->addProvider('provider-test', $provider);
|
$this->addProvider('provider-test', $provider);
|
||||||
@@ -1203,17 +1438,17 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
|||||||
->method('onCallback');
|
->method('onCallback');
|
||||||
|
|
||||||
$token = new Token($provider, 42);
|
$token = new Token($provider, 42);
|
||||||
$this->addUsrAuthDoctrineEntitySupport(42, null);
|
|
||||||
|
|
||||||
$provider->expects($this->once())
|
$provider->expects($this->any())
|
||||||
->method('getToken')
|
->method('getToken')
|
||||||
->will($this->returnValue($token));
|
->will($this->returnValue($token));
|
||||||
|
|
||||||
$user = $this->getMockBuilder('\User_Adapter')
|
self::$DI['app']['authentication.suggestion-finder'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\SuggestionFinder')
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
|
|
||||||
$this->mockSuggestionFinder();
|
$user = self::$DI['user'];
|
||||||
|
|
||||||
self::$DI['app']['authentication.suggestion-finder']->expects($this->once())
|
self::$DI['app']['authentication.suggestion-finder']->expects($this->once())
|
||||||
->method('find')
|
->method('find')
|
||||||
->with($token)
|
->with($token)
|
||||||
@@ -1223,10 +1458,19 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
|||||||
self::$DI['client']->request('GET', '/login/provider/provider-test/callback/');
|
self::$DI['client']->request('GET', '/login/provider/provider-test/callback/');
|
||||||
|
|
||||||
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
||||||
$this->assertSame('/login/provider/provider-test/add-mapping/?id=42', self::$DI['client']->getResponse()->headers->get('location'));
|
|
||||||
|
$ret = self::$DI['app']['EM']->getRepository('\Entities\UsrAuthProvider')
|
||||||
|
->findBy(array('usr_id' => self::$DI['user']->get_id(), 'provider' => 'provider-test'));
|
||||||
|
|
||||||
|
$this->assertCount(1, $ret);
|
||||||
|
|
||||||
|
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
||||||
|
$this->assertSame('/prod/', self::$DI['client']->getResponse()->headers->get('location'));
|
||||||
|
|
||||||
|
$this->assertTrue(self::$DI['app']['authentication']->isAuthenticated());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAuthenticateProviderCallbackWithoutSuggestion()
|
public function testAuthenticateProviderCallbackWithAccountCreatorEnabled()
|
||||||
{
|
{
|
||||||
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
||||||
$this->addProvider('provider-test', $provider);
|
$this->addProvider('provider-test', $provider);
|
||||||
@@ -1235,221 +1479,158 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
|||||||
->method('onCallback');
|
->method('onCallback');
|
||||||
|
|
||||||
$token = new Token($provider, 42);
|
$token = new Token($provider, 42);
|
||||||
$this->addUsrAuthDoctrineEntitySupport(42, null);
|
|
||||||
|
|
||||||
$provider->expects($this->once())
|
$provider->expects($this->any())
|
||||||
->method('getToken')
|
->method('getToken')
|
||||||
->will($this->returnValue($token));
|
->will($this->returnValue($token));
|
||||||
|
|
||||||
$this->mockSuggestionFinder();
|
self::$DI['app']['authentication.suggestion-finder'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\SuggestionFinder')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
self::$DI['app']['authentication.suggestion-finder']->expects($this->once())
|
self::$DI['app']['authentication.suggestion-finder']->expects($this->once())
|
||||||
->method('find')
|
->method('find')
|
||||||
->with($token)
|
->with($token)
|
||||||
->will($this->returnValue(null));
|
->will($this->returnValue(null));
|
||||||
|
|
||||||
|
$identity = $this->getMockBuilder('Alchemy\Phrasea\Authentication\Provider\Token\Identity')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$provider->expects($this->any())
|
||||||
|
->method('getIdentity')
|
||||||
|
->will($this->returnValue($identity));
|
||||||
|
|
||||||
|
$provider->expects($this->once())
|
||||||
|
->method('getTemplates')
|
||||||
|
->will($this->returnValue(array()));
|
||||||
|
|
||||||
|
$identity->expects($this->once())
|
||||||
|
->method('getEmail')
|
||||||
|
->will($this->returnValue('supermail@superprovider.com'));
|
||||||
|
|
||||||
|
$createdUserId = \User_Adapter::get_usr_id_from_email(self::$DI['app'], 'supermail@superprovider.com');
|
||||||
|
|
||||||
|
if (false === $createdUserId) {
|
||||||
|
$random = self::$DI['app']['tokens']->generatePassword();
|
||||||
|
$createdUser = \User_Adapter::create(self::$DI['app'], 'temporary-'.$random, $random, 'supermail@superprovider.com', false);
|
||||||
|
} else {
|
||||||
|
$createdUser = \User_Adapter::getInstance($createdUserId, self::$DI['app']);
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$DI['app']['authentication.providers.account-creator'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\AccountCreator')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
self::$DI['app']['authentication.providers.account-creator']->expects($this->once())
|
||||||
|
->method('create')
|
||||||
|
->with(self::$DI['app'], 42, 'supermail@superprovider.com', array())
|
||||||
|
->will($this->returnValue($createdUser));
|
||||||
|
self::$DI['app']['authentication.providers.account-creator']->expects($this->once())
|
||||||
|
->method('isEnabled')
|
||||||
|
->will($this->returnValue(true));
|
||||||
|
|
||||||
self::$DI['app']['authentication']->closeAccount();
|
self::$DI['app']['authentication']->closeAccount();
|
||||||
self::$DI['client']->request('GET', '/login/provider/provider-test/callback/');
|
self::$DI['client']->request('GET', '/login/provider/provider-test/callback/');
|
||||||
|
|
||||||
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
||||||
$this->assertSame('/login/provider/provider-test/bind-account/?id=42', self::$DI['client']->getResponse()->headers->get('location'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetBindAccountWithSuccess()
|
$ret = self::$DI['app']['EM']->getRepository('\Entities\UsrAuthProvider')
|
||||||
{
|
->findBy(array('usr_id' => $createdUser->get_id(), 'provider' => 'provider-test'));
|
||||||
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
|
||||||
$this->addProvider('provider-test', $provider);
|
|
||||||
|
|
||||||
$token = new Token($provider, 42);
|
$this->assertCount(1, $ret);
|
||||||
|
|
||||||
$provider->expects($this->once())
|
|
||||||
->method('getToken')
|
|
||||||
->will($this->returnValue($token));
|
|
||||||
|
|
||||||
$provider->expects($this->any())
|
|
||||||
->method('getIdentity')
|
|
||||||
->will($this->returnValue(new Identity()));
|
|
||||||
|
|
||||||
self::$DI['app']['authentication']->closeAccount();
|
|
||||||
self::$DI['client']->request('GET', '/login/provider/provider-test/bind-account/');
|
|
||||||
|
|
||||||
$this->assertSame(200, self::$DI['client']->getResponse()->getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPostBindAccountWithSuccess()
|
|
||||||
{
|
|
||||||
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
|
||||||
$this->addProvider('provider-test', $provider);
|
|
||||||
|
|
||||||
$token = new Token($provider, 42);
|
|
||||||
|
|
||||||
$provider->expects($this->once())
|
|
||||||
->method('getToken')
|
|
||||||
->will($this->returnValue($token));
|
|
||||||
|
|
||||||
$password = \random::generatePassword();
|
|
||||||
|
|
||||||
$login = self::$DI['app']['authentication']->getUser()->get_login();
|
|
||||||
self::$DI['app']['authentication']->getUser()->set_password($password);
|
|
||||||
self::$DI['app']['authentication']->getUser()->set_mail_locked(false);
|
|
||||||
|
|
||||||
self::$DI['app']['authentication']->closeAccount();
|
|
||||||
self::$DI['client']->request('POST', '/login/provider/provider-test/bind-account/', array(
|
|
||||||
'_token' => 'token',
|
|
||||||
'login' => $login,
|
|
||||||
'password' => $password,
|
|
||||||
'remember-me' => '1',
|
|
||||||
'redirect' => '/prod/',
|
|
||||||
));
|
|
||||||
|
|
||||||
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
||||||
$this->assertSame('/prod/', self::$DI['client']->getResponse()->headers->get('location'));
|
$this->assertSame('/prod/', self::$DI['client']->getResponse()->headers->get('location'));
|
||||||
|
|
||||||
$entity = self::$DI['app']['EM']->getRepository('Entities\UsrAuthProvider')
|
$this->assertTrue(self::$DI['app']['authentication']->isAuthenticated());
|
||||||
->findOneBy(array('distant_id' => 42));
|
|
||||||
|
|
||||||
$this->assertNotNull($entity);
|
$createdUser->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPostBindAccountWithFailure()
|
public function testAuthenticateProviderCallbackWithRegistrationEnabled()
|
||||||
{
|
{
|
||||||
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
||||||
$this->addProvider('provider-test', $provider);
|
$this->addProvider('provider-test', $provider);
|
||||||
|
|
||||||
$provider->expects($this->never())
|
|
||||||
->method('getToken');
|
|
||||||
|
|
||||||
$login = self::$DI['app']['authentication']->getUser()->get_login();
|
|
||||||
|
|
||||||
self::$DI['app']['authentication']->closeAccount();
|
|
||||||
self::$DI['client']->request('POST', '/login/provider/provider-test/bind-account/', array(
|
|
||||||
'_token' => 'token',
|
|
||||||
'login' => $login,
|
|
||||||
'password' => '',
|
|
||||||
'remember-me' => '1',
|
|
||||||
'redirect' => '/prod/',
|
|
||||||
));
|
|
||||||
|
|
||||||
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
|
||||||
$this->assertSame('/login/provider/provider-test/add-mapping/', self::$DI['client']->getResponse()->headers->get('location'));
|
|
||||||
|
|
||||||
$entity = self::$DI['app']['EM']->getRepository('Entities\UsrAuthProvider')
|
|
||||||
->findOneBy(array('distant_id' => 42));
|
|
||||||
|
|
||||||
$this->assertNull($entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPostAddMapWithSuccess()
|
|
||||||
{
|
|
||||||
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
|
||||||
$this->addProvider('provider-test', $provider);
|
|
||||||
|
|
||||||
$token = new Token($provider, 42);
|
|
||||||
|
|
||||||
$provider->expects($this->once())
|
$provider->expects($this->once())
|
||||||
->method('getToken')
|
->method('onCallback');
|
||||||
->will($this->returnValue($token));
|
|
||||||
|
|
||||||
$password = \random::generatePassword();
|
|
||||||
|
|
||||||
$login = self::$DI['app']['authentication']->getUser()->get_login();
|
|
||||||
self::$DI['app']['authentication']->getUser()->set_password($password);
|
|
||||||
self::$DI['app']['authentication']->getUser()->set_mail_locked(false);
|
|
||||||
|
|
||||||
self::$DI['app']['authentication']->closeAccount();
|
|
||||||
self::$DI['client']->request('POST', '/login/provider/provider-test/add-mapping/', array(
|
|
||||||
'_token' => 'token',
|
|
||||||
'login' => $login,
|
|
||||||
'password' => $password,
|
|
||||||
'remember-me' => '1',
|
|
||||||
'redirect' => '/prod/',
|
|
||||||
));
|
|
||||||
|
|
||||||
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
|
||||||
$this->assertSame('/prod/', self::$DI['client']->getResponse()->headers->get('location'));
|
|
||||||
|
|
||||||
$entity = self::$DI['app']['EM']->getRepository('Entities\UsrAuthProvider')
|
|
||||||
->findOneBy(array('distant_id' => 42));
|
|
||||||
|
|
||||||
$this->assertNotNull($entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPostAddMapWithFailure()
|
|
||||||
{
|
|
||||||
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
|
||||||
$this->addProvider('provider-test', $provider);
|
|
||||||
|
|
||||||
$provider->expects($this->never())
|
|
||||||
->method('getToken');
|
|
||||||
|
|
||||||
$login = self::$DI['app']['authentication']->getUser()->get_login();
|
|
||||||
|
|
||||||
self::$DI['app']['authentication']->closeAccount();
|
|
||||||
self::$DI['client']->request('POST', '/login/provider/provider-test/add-mapping/', array(
|
|
||||||
'_token' => 'token',
|
|
||||||
'login' => $login,
|
|
||||||
'password' => '',
|
|
||||||
'remember-me' => '1',
|
|
||||||
'redirect' => '/prod/',
|
|
||||||
));
|
|
||||||
|
|
||||||
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
|
||||||
$this->assertSame('/login/provider/provider-test/add-mapping/', self::$DI['client']->getResponse()->headers->get('location'));
|
|
||||||
|
|
||||||
$this->assertFlashMessagePopulated(self::$DI['app'], 'error', 1);
|
|
||||||
|
|
||||||
$entity = self::$DI['app']['EM']->getRepository('Entities\UsrAuthProvider')
|
|
||||||
->findOneBy(array('distant_id' => 42));
|
|
||||||
|
|
||||||
$this->assertNull($entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetAddMappingWithSuccess()
|
|
||||||
{
|
|
||||||
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
|
||||||
$this->addProvider('provider-test', $provider);
|
|
||||||
|
|
||||||
$token = new Token($provider, 42);
|
$token = new Token($provider, 42);
|
||||||
|
|
||||||
$provider->expects($this->once())
|
|
||||||
->method('getToken')
|
|
||||||
->will($this->returnValue($token));
|
|
||||||
|
|
||||||
$provider->expects($this->any())
|
$provider->expects($this->any())
|
||||||
->method('getIdentity')
|
->method('getToken')
|
||||||
->will($this->returnValue(new Identity()));
|
->will($this->returnValue($token));
|
||||||
|
|
||||||
$this->mockSuggestionFinder();
|
self::$DI['app']['authentication.suggestion-finder'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\SuggestionFinder')
|
||||||
|
|
||||||
$user = $this->getMockBuilder('\User_Adapter')
|
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
|
|
||||||
self::$DI['app']['authentication.suggestion-finder']->expects($this->once())
|
self::$DI['app']['authentication.suggestion-finder']->expects($this->once())
|
||||||
->method('find')
|
->method('find')
|
||||||
->with($token)
|
->with($token)
|
||||||
->will($this->returnValue($user));
|
->will($this->returnValue(null));
|
||||||
|
|
||||||
|
self::$DI['app']['authentication.providers.account-creator'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\AccountCreator')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
self::$DI['app']['authentication.providers.account-creator']->expects($this->never())
|
||||||
|
->method('create');
|
||||||
|
self::$DI['app']['authentication.providers.account-creator']->expects($this->once())
|
||||||
|
->method('isEnabled')
|
||||||
|
->will($this->returnValue(false));
|
||||||
|
|
||||||
|
self::$DI['app']['registration.enabled'] = true;
|
||||||
|
|
||||||
self::$DI['app']['authentication']->closeAccount();
|
self::$DI['app']['authentication']->closeAccount();
|
||||||
self::$DI['client']->request('GET', '/login/provider/provider-test/add-mapping/');
|
self::$DI['client']->request('GET', '/login/provider/provider-test/callback/');
|
||||||
|
|
||||||
$this->assertSame(200, self::$DI['client']->getResponse()->getStatusCode());
|
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
||||||
|
$this->assertSame('/login/register-classic/?providerId=provider-test', self::$DI['client']->getResponse()->headers->get('location'));
|
||||||
|
|
||||||
|
$this->assertFalse(self::$DI['app']['authentication']->isAuthenticated());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetAddMappingFailingAuthentication()
|
public function testAuthenticateProviderCallbackWithoutRegistrationEnabled()
|
||||||
{
|
{
|
||||||
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
||||||
$this->addProvider('provider-test', $provider);
|
$this->addProvider('provider-test', $provider);
|
||||||
|
|
||||||
$provider->expects($this->once())
|
$provider->expects($this->once())
|
||||||
|
->method('onCallback');
|
||||||
|
|
||||||
|
$token = new Token($provider, 42);
|
||||||
|
|
||||||
|
$provider->expects($this->any())
|
||||||
->method('getToken')
|
->method('getToken')
|
||||||
->will($this->throwException(new NotAuthenticatedException('Not authenticated !')));
|
->will($this->returnValue($token));
|
||||||
|
|
||||||
|
self::$DI['app']['authentication.suggestion-finder'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\SuggestionFinder')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
self::$DI['app']['authentication.suggestion-finder']->expects($this->once())
|
||||||
|
->method('find')
|
||||||
|
->with($token)
|
||||||
|
->will($this->returnValue(null));
|
||||||
|
|
||||||
|
self::$DI['app']['authentication.providers.account-creator'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\AccountCreator')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
self::$DI['app']['authentication.providers.account-creator']->expects($this->never())
|
||||||
|
->method('create');
|
||||||
|
self::$DI['app']['authentication.providers.account-creator']->expects($this->once())
|
||||||
|
->method('isEnabled')
|
||||||
|
->will($this->returnValue(false));
|
||||||
|
|
||||||
|
self::$DI['app']['registration.enabled'] = false;
|
||||||
|
|
||||||
self::$DI['app']['authentication']->closeAccount();
|
self::$DI['app']['authentication']->closeAccount();
|
||||||
self::$DI['client']->request('GET', '/login/provider/provider-test/add-mapping/');
|
self::$DI['client']->request('GET', '/login/provider/provider-test/callback/');
|
||||||
|
|
||||||
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
$this->assertSame(302, self::$DI['client']->getResponse()->getStatusCode());
|
||||||
$this->assertSame('/login/', self::$DI['client']->getResponse()->headers->get('location'));
|
$this->assertSame('/login/', self::$DI['client']->getResponse()->headers->get('location'));
|
||||||
|
|
||||||
|
$this->assertFalse(self::$DI['app']['authentication']->isAuthenticated());
|
||||||
$this->assertFlashMessagePopulated(self::$DI['app'], 'error', 1);
|
$this->assertFlashMessagePopulated(self::$DI['app'], 'error', 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1502,16 +1683,14 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
|||||||
|
|
||||||
private function addUsrAuthDoctrineEntitySupport($id, $out, $participants = false)
|
private function addUsrAuthDoctrineEntitySupport($id, $out, $participants = false)
|
||||||
{
|
{
|
||||||
$repo = $this->getMockBuilder('Doctrine\ORM\EntityRepository')
|
$repo = $this->getMockBuilder('Doctrine\ORM\EntityRepository\UsrAuthProviderRepository')
|
||||||
|
->setMethods(array('findWithProviderAndId'))
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
|
|
||||||
$repo->expects($this->once())
|
$repo->expects($this->any())
|
||||||
->method('findOneBy')
|
->method('findWithProviderAndId')
|
||||||
->with($this->equalTo(array(
|
->with('provider-test', $id)
|
||||||
'provider' => 'provider-test',
|
|
||||||
'distant_id' => $id
|
|
||||||
)))
|
|
||||||
->will($this->returnValue($out));
|
->will($this->returnValue($out));
|
||||||
|
|
||||||
self::$DI['app']['EM'] = $this->getMockBuilder('Doctrine\ORM\EntityManager')
|
self::$DI['app']['EM'] = $this->getMockBuilder('Doctrine\ORM\EntityManager')
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Alchemy\Tests\Phrasea\Form\Login;
|
|
||||||
|
|
||||||
use Alchemy\Phrasea\Form\Login\PhraseaAuthenticationWithMappingForm;;
|
|
||||||
use Alchemy\Tests\Phrasea\Form\FormTestCase;
|
|
||||||
|
|
||||||
class PhraseaAuthenticationWithMappingFormTest extends FormTestCase
|
|
||||||
{
|
|
||||||
protected function getForm()
|
|
||||||
{
|
|
||||||
return new PhraseaAuthenticationWithMappingForm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -51,7 +51,7 @@ class PhraseaRegisterFormTest extends FormTestCase
|
|||||||
|
|
||||||
$form = new PhraseaRegisterForm(self::$DI['app'], $available, $params, new Camelizer());
|
$form = new PhraseaRegisterForm(self::$DI['app'], $available, $params, new Camelizer());
|
||||||
|
|
||||||
$this->assertCount(8, self::$DI['app']->form($form)->createView()->vars['form']->children);
|
$this->assertCount(9, self::$DI['app']->form($form)->createView()->vars['form']->children);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFormDoesNotRegisterNonValidFields()
|
public function testFormDoesNotRegisterNonValidFields()
|
||||||
@@ -75,6 +75,6 @@ class PhraseaRegisterFormTest extends FormTestCase
|
|||||||
|
|
||||||
$form = new PhraseaRegisterForm(self::$DI['app'], $available, $params, new Camelizer());
|
$form = new PhraseaRegisterForm(self::$DI['app'], $available, $params, new Camelizer());
|
||||||
|
|
||||||
$this->assertCount(7, self::$DI['app']->form($form)->createView()->vars['form']->children);
|
$this->assertCount(8, self::$DI['app']->form($form)->createView()->vars['form']->children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -910,6 +910,21 @@ abstract class PhraseanetPHPUnitAbstract extends WebTestCase
|
|||||||
$expectedMails[get_class($email)]++;
|
$expectedMails[get_class($email)]++;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createRandomMock()
|
||||||
|
{
|
||||||
|
return $this->getMockBuilder('\random')
|
||||||
|
->setMethods(array('generatePassword'))
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createAppboxMock()
|
||||||
|
{
|
||||||
|
return $this->getMockBuilder('appbox')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CsrfTestProvider implements CsrfProviderInterface
|
class CsrfTestProvider implements CsrfProviderInterface
|
||||||
|
|||||||
Reference in New Issue
Block a user