diff --git a/lib/Alchemy/Phrasea/Application/Root.php b/lib/Alchemy/Phrasea/Application/Root.php index 88a89b201f..8169b598cb 100644 --- a/lib/Alchemy/Phrasea/Application/Root.php +++ b/lib/Alchemy/Phrasea/Application/Root.php @@ -57,7 +57,6 @@ return call_user_func(function() { $app->mount('/feeds/', new RSSFeeds()); $app->mount('/account/', new Account()); - $app->mount('/login/authenticate/', new AuthenticateController()); $app->mount('/login/', new Login()); $app->mount('/developers/', new Developers()); diff --git a/lib/Alchemy/Phrasea/Controller/Login/Authenticate.php b/lib/Alchemy/Phrasea/Controller/Login/Authenticate.php deleted file mode 100644 index af8cb7a7b8..0000000000 --- a/lib/Alchemy/Phrasea/Controller/Login/Authenticate.php +++ /dev/null @@ -1,113 +0,0 @@ -post('/', __CLASS__ . '::authenticate') - ->before(function() use ($app) { - return $app['phraseanet.core']['Firewall']->requireNotAuthenticated($app); - }); - - return $controllers; - } - - public function authenticate(Application $app, Request $request) - { - /* @var $Core \Alchemy\Phrasea\Core */ - $Core = $app['phraseanet.core']; - - $appbox = \appbox::get_instance($Core); - $session = $appbox->get_session(); - $registry = $appbox->get_registry(); - - if ($registry->get('GV_captchas') - && trim($registry->get('GV_captcha_private_key')) !== '' - && trim($registry->get('GV_captcha_public_key')) !== '') - include($registry->get('GV_RootPath') . 'lib/vendor/recaptcha/recaptchalib.php'); - - $is_guest = false; - - if (null !== $request->get('nolog') && \phrasea::guest_allowed()) { - $is_guest = true; - } - - if ((null !== $request->get('login') && null !== $request->get('pwd')) || $is_guest) { - - /** - * @todo dispatch an event that can be used to tweak the authentication - * (LDAP....) - */ - // $app['dispatcher']->dispatch(); - - try { - if ($is_guest) { - $auth = new \Session_Authentication_Guest($appbox); - } else { - $captcha = false; - - if ($registry->get('GV_captchas') - && trim($registry->get('GV_captcha_private_key')) !== '' - && trim($registry->get('GV_captcha_public_key')) !== '' - && ! is_null($request->get("recaptcha_challenge_field") - && ! is_null($request->get("recaptcha_response_field")))) { - $checkCaptcha = recaptcha_check_answer($registry->get('GV_captcha_private_key'), $_SERVER["REMOTE_ADDR"], $request->get("recaptcha_challenge_field"), $request->get("recaptcha_response_field")); - - if ($checkCaptcha->is_valid) { - $captcha = true; - } - } - - $auth = new \Session_Authentication_Native($appbox, $request->get('login'), $request->get('pwd')); - $auth->set_captcha_challenge($captcha); - } - $session->authenticate($auth); - } catch (\Exception_Session_StorageClosed $e) { - return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=session"); - } catch (\Exception_Session_RequireCaptcha $e) { - return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=captcha"); - } catch (\Exception_Unauthorized $e) { - return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=auth"); - } catch (\Exception_Session_MailLocked $e) { - return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=mail-not-confirmed&usr=" . $e->get_usr_id()); - } catch (\Exception_Session_WrongToken $e) { - return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=token"); - } catch (\Exception_InternalServerError $e) { - return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=session"); - } catch (\Exception_ServiceUnavailable $e) { - return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=maintenance"); - } catch (\Exception_Session_BadSalinity $e) { - $date = new \DateTime('5 minutes'); - $usr_id = \User_Adapter::get_usr_id_from_login($request->get('login')); - $url = \random::getUrlToken(\random::TYPE_PASSWORD, $usr_id, $date); - - $url = '/account/forgot-password/?token=' . $url . '&salt=1'; - - return $app->redirect($url); - } catch (\Exception $e) { - return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=" . _('An error occured')); - } - - if ($app['browser']->isMobile()) { - return $app->redirect("/lightbox/"); - } elseif ($request->get('redirect')) { - return $app->redirect($request->get('redirect')); - } elseif (true !== $app['browser']->isNewGeneration()) { - return $app->redirect('/client/'); - } else { - return $app->redirect('/prod/'); - } - } else { - return $app->redirect("/login/"); - } - } -} diff --git a/lib/Alchemy/Phrasea/Controller/Root/Account.php b/lib/Alchemy/Phrasea/Controller/Root/Account.php index 87b641db7e..dc280a89b1 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Account.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Account.php @@ -13,7 +13,9 @@ namespace Alchemy\Phrasea\Controller\Root; use Silex\Application; use Silex\ControllerProviderInterface; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; /** @@ -45,7 +47,8 @@ class Account implements ControllerProviderInterface * * return : HTML Response */ - $controllers->get('/', $this->call('displayAccount'))->bind('account'); + $controllers->get('/', $this->call('displayAccount')) + ->bind('account'); /** * Update account route @@ -152,7 +155,8 @@ class Account implements ControllerProviderInterface * * return : HTML Response */ - $controllers->get('/access/', $this->call('accountAccess'))->bind('account_access'); + $controllers->get('/access/', $this->call('accountAccess')) + ->bind('account_access'); /** * Give authorized applications that can access user informations @@ -167,7 +171,8 @@ class Account implements ControllerProviderInterface * * return : HTML Response */ - $controllers->post('/reset-email/', $this->call('resetEmail'))->bind('reset_email'); + $controllers->post('/reset-email/', $this->call('resetEmail')) + ->bind('reset_email'); /** * Grant access to an authorized app @@ -182,7 +187,8 @@ class Account implements ControllerProviderInterface * * return : HTML Response */ - $controllers->get('/reset-password/', $this->call('resetPassword'))->bind('reset_password'); + $controllers->get('/reset-password/', $this->call('resetPassword')) + ->bind('reset_password'); /** * Give account open sessions @@ -239,9 +245,9 @@ class Account implements ControllerProviderInterface /** * Reset Password * - * @param \Silex\Application $app - * @param \Symfony\Component\HttpFoundation\Request $request - * @return \Symfony\Component\HttpFoundation\Response + * @param Application $app + * @param Request $request + * @return Response */ public function resetPassword(Application $app, Request $request) { @@ -267,9 +273,9 @@ class Account implements ControllerProviderInterface /** * Reset Email * - * @param \Silex\Application $app - * @param \Symfony\Component\HttpFoundation\Request $request - * @return \Symfony\Component\HttpFoundation\JsonResponse + * @param Application $app + * @param Request $request + * @return RedirectResponse */ public function resetEmail(Application $app, Request $request) { @@ -326,9 +332,9 @@ class Account implements ControllerProviderInterface /** * Display reset email form * - * @param \Silex\Application $app - * @param \Symfony\Component\HttpFoundation\Request $request - * @return \Symfony\Component\HttpFoundation\JsonResponse + * @param Application $app + * @param Request $request + * @return Response */ public function displayResetEmailForm(Application $app, Request $request) { @@ -372,9 +378,9 @@ class Account implements ControllerProviderInterface /** * Submit the new password * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return RedirectResponse */ public function renewPassword(Application $app, Request $request) { @@ -410,10 +416,9 @@ class Account implements ControllerProviderInterface /** * Display authorized applications that can access user informations * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return JsonResponse */ public function grantAccess(Application $app, Request $request, $application_id) { @@ -442,10 +447,9 @@ class Account implements ControllerProviderInterface /** * Display account base access * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response */ public function accountAccess(Application $app, Request $request) { @@ -459,10 +463,9 @@ class Account implements ControllerProviderInterface /** * Display authorized applications that can access user informations * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response */ public function accountAuthorizedApps(Application $app, Request $request) { @@ -474,10 +477,9 @@ class Account implements ControllerProviderInterface /** * Display account session accesss * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response */ public function accountSessionsAccess(Application $app, Request $request) { @@ -487,10 +489,9 @@ class Account implements ControllerProviderInterface /** * Display account form * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response */ public function displayAccount(Application $app, Request $request) { @@ -530,7 +531,6 @@ class Account implements ControllerProviderInterface * * @param Application $app A Silex application where the controller is mounted on * @param Request $request The current request - * * @return Response */ public function updateAccount(Application $app, Request $request) @@ -550,7 +550,7 @@ class Account implements ControllerProviderInterface $register->add_request($user, \collection::get_from_base_id($baseId)); $notice = 'demand-ok'; } catch (\Exception $e) { - exit($e->getMessage()); + } } } @@ -630,7 +630,7 @@ class Account implements ControllerProviderInterface $notifId = $notification['id']; $notifName = sprintf('notification_%d', $notifId); - if (in_array($notifId, $requestedNotifications)) { + if (isset($requestedNotifications[$notifId])) { $user->setPrefs($notifName, '1'); } else { $user->setPrefs($notifName, '0'); diff --git a/lib/Alchemy/Phrasea/Controller/Root/Developers.php b/lib/Alchemy/Phrasea/Controller/Root/Developers.php index 573d6ac6d4..f401005cee 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Developers.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Developers.php @@ -13,7 +13,9 @@ namespace Alchemy\Phrasea\Controller\Root; use Silex\Application; use Silex\ControllerProviderInterface; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; /** @@ -175,11 +177,10 @@ class Developers implements ControllerProviderInterface /** * Delete application * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * @param integer $id The application id - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @param integer $id The application id + * @return JsonResponse */ public function deleteApp(Application $app, Request $request, $id) { @@ -202,11 +203,10 @@ class Developers implements ControllerProviderInterface /** * Change application callback * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * @param integer $id The application id - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @param integer $id The application id + * @return JsonResponse */ public function renewAppCallback(Application $app, Request $request, $id) { @@ -234,11 +234,10 @@ class Developers implements ControllerProviderInterface /** * Authorize application to use a grant password type * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * @param integer $id The application id - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @param integer $id The application id + * @return JsonResponse */ public function renewAccessToken(Application $app, Request $request, $id) { @@ -273,11 +272,10 @@ class Developers implements ControllerProviderInterface /** * Authorize application to use a grant password type * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * @param integer $id The application id - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @param integer $id The application id + * @return JsonResponse */ public function authorizeGrantpassword(Application $app, Request $request, $id) { @@ -300,14 +298,13 @@ class Developers implements ControllerProviderInterface /** * Create a new developer applications * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response */ public function newApp(Application $app, Request $request) { - if ($request->get('type') == "desktop") { + if ($request->get('type') === \API_OAuth2_Application::DESKTOP_TYPE) { $form = new \API_OAuth2_Form_DevAppDesktop($app['request']); } else { $form = new \API_OAuth2_Form_DevAppInternet($app['request']); @@ -337,10 +334,9 @@ class Developers implements ControllerProviderInterface /** * List of apps created by the user * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response */ public function listApps(Application $app, Request $request) { @@ -353,10 +349,9 @@ class Developers implements ControllerProviderInterface /** * Display form application * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response */ public function displayFormApp(Application $app, Request $request) { @@ -370,11 +365,10 @@ class Developers implements ControllerProviderInterface /** * Get application information * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * @param integer $id The application id - * - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @param integer $id The application id + * @return Response */ public function getApp(Application $app, Request $request, $id) { diff --git a/lib/Alchemy/Phrasea/Controller/Root/Login.php b/lib/Alchemy/Phrasea/Controller/Root/Login.php index 0311494f2e..b0624e5b5e 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Login.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Login.php @@ -15,6 +15,7 @@ use Alchemy\Phrasea\Core; use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; /** @@ -44,18 +45,47 @@ class Login implements ControllerProviderInterface */ $controllers->get('/', $this->call('login')) ->before(function() use ($app) { -// -// if ( ! $app['phraseanet.appbox']->get_session()->isset_postlog() -// && $app['phraseanet.core']->isAuthenticated() -// && $app['request']->get('error') != 'no-connection') { -// -// return $app->redirect($app['request']->get('redirect', '/prod/')); -// } - return $app['phraseanet.core']['Firewall']->requireNotAuthenticated($app); + if (null !== $app['request']->get('postlog')) { + + // if isset postlog parameter, set cookie and log out current user + // then post login operation like getting baskets from an invit session + // could be done by Session_handler authentication process + + $app['phraseanet.appbox']->get_session()->set_postlog(); + + return $app->redirect("/login/logout/?redirect=" . $app['request']->get('redirect', 'prod')); + } + + + if ($app['phraseanet.core']->isAuthenticated()) { + + return $app->redirect('/' . $app['request']->get('redirect', 'prod') . '/'); + } }) ->bind('homepage'); + /** + * Authenticate + * + * name : login_authenticate + * + * description : authenticate to phraseanet + * + * method : POST + * + * parameters : none + * + * return : HTML Response + */ + $controllers->post('/authenticate/', $this->call('authenticate')) + ->before(function() use ($app) { + if ($app['phraseanet.core']->isAuthenticated()) { + return $app->redirect('/prod/'); + } + }) + ->bind('login_authenticate'); + /** * Logout * @@ -174,9 +204,9 @@ class Login implements ControllerProviderInterface /** * Send a confirmation mail after register * - * @param \Silex\Application $app - * @param \Symfony\Component\HttpFoundation\Request $request - * @return \Symfony\Component\HttpFoundation\Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return RedirectResponse */ public function sendConfirmMail(Application $app, Request $request) { @@ -201,9 +231,9 @@ class Login implements ControllerProviderInterface /** * Validation of email adress * - * @param \Silex\Application $app - * @param \Symfony\Component\HttpFoundation\Request $request - * @return \Symfony\Component\HttpFoundation\Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return RedirectResponse */ public function registerConfirm(Application $app, Request $request) { @@ -262,9 +292,9 @@ class Login implements ControllerProviderInterface /** * Submit the new password * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return RedirectResponse */ public function renewPassword(Application $app, Request $request) { @@ -327,9 +357,9 @@ class Login implements ControllerProviderInterface /** * Get the fogot password form * - * @param Application $app A Silex application where the controller is mounted on - * @param Request $request The current request - * @return Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response */ public function displayForgotPasswordForm(Application $app, Request $request) { @@ -398,9 +428,9 @@ class Login implements ControllerProviderInterface /** * Get the register form * - * @param \Silex\Application $app - * @param \Symfony\Component\HttpFoundation\Request $request - * @return \Symfony\Component\HttpFoundation\Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response */ public function displayRegisterForm(Application $app, Request $request) { @@ -458,9 +488,9 @@ class Login implements ControllerProviderInterface /** * Get the register form * - * @param \Silex\Application $app - * @param \Symfony\Component\HttpFoundation\Request $request - * @return \Symfony\Component\HttpFoundation\Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return RedirectResponse */ public function register(Application $app, Request $request) { @@ -617,9 +647,9 @@ class Login implements ControllerProviderInterface /** * Logout from Phraseanet * - * @param \Silex\Application $app - * @param \Symfony\Component\HttpFoundation\Request $request - * @return \Symfony\Component\HttpFoundation\Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return RedirectResponse */ public function logout(Application $app, Request $request) { @@ -640,27 +670,20 @@ class Login implements ControllerProviderInterface /** * Login into Phraseanet * - * @param \Silex\Application $app - * @param \Symfony\Component\HttpFoundation\Request $request - * @return \Symfony\Component\HttpFoundation\Response + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response */ public function login(Application $app, Request $request) { $appbox = $app['phraseanet.appbox']; - $session = $appbox->get_session(); - $registry = $appbox->get_registry(); + $registry = $app['phraseanet.core']['Registry']; require_once($registry->get('GV_RootPath') . 'lib/classes/deprecated/inscript.api.php'); if ($registry->get('GV_captchas') && trim($registry->get('GV_captcha_private_key')) !== '' && trim($registry->get('GV_captcha_public_key')) !== '') { include($registry->get('GV_RootPath') . 'lib/vendor/recaptcha/recaptchalib.php'); } - if ($request->get('postlog')) { - $session->set_postlog(true); - - return $app->redirect("/login/?redirect=" . $request->get('redirect')); - } - $warning = $request->get('error', ''); try { @@ -758,6 +781,97 @@ class Login implements ControllerProviderInterface )); } + /** + * Authenticate to phraseanet + * + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return RedirectResponse + */ + public function authenticate(Application $app, Request $request) + { + $appbox = $app['phraseanet.appbox']; + $session = $appbox->get_session(); + $registry = $app['phraseanet.core']['Registry']; + + $is_guest = false; + + if (null !== $request->get('nolog') && \phrasea::guest_allowed()) { + $is_guest = true; + } + + if (((null !== $login = $request->get('login')) && (null !== $pwd = $request->get('pwd'))) || $is_guest) { + + /** + * @todo dispatch an event that can be used to tweak the authentication + * (LDAP....) + */ + // $app['dispatcher']->dispatch(); + + try { + if ($is_guest) { + $auth = new \Session_Authentication_Guest($appbox); + } else { + $captcha = false; + + if ($registry->get('GV_captchas') + && '' !== $privateKey = trim($registry->get('GV_captcha_private_key')) + && trim($registry->get('GV_captcha_public_key')) !== '' + && null !== $challenge = $request->get("recaptcha_challenge_field") + && null !== $captachResponse = $request->get("recaptcha_response_field")) { + + include($registry->get('GV_RootPath') . 'lib/vendor/recaptcha/recaptchalib.php'); + + $checkCaptcha = recaptcha_check_answer($privateKey, $_SERVER["REMOTE_ADDR"], $challenge, $captachResponse); + + if ($checkCaptcha->is_valid) { + $captcha = true; + } + } + + $auth = new \Session_Authentication_Native($appbox, $login, $pwd); + $auth->set_captcha_challenge($captcha); + } + + $session->authenticate($auth); + } catch (\Exception_Session_StorageClosed $e) { + return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=session"); + } catch (\Exception_Session_RequireCaptcha $e) { + return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=captcha"); + } catch (\Exception_Unauthorized $e) { + return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=auth"); + } catch (\Exception_Session_MailLocked $e) { + return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=mail-not-confirmed&usr=" . $e->get_usr_id()); + } catch (\Exception_Session_WrongToken $e) { + return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=token"); + } catch (\Exception_InternalServerError $e) { + return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=session"); + } catch (\Exception_ServiceUnavailable $e) { + return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=maintenance"); + } catch (\Exception_Session_BadSalinity $e) { + $date = new \DateTime('5 minutes'); + $usr_id = \User_Adapter::get_usr_id_from_login($request->get('login')); + $url = '/account/forgot-password/?token=' . \random::getUrlToken(\random::TYPE_PASSWORD, $usr_id, $date) . '&salt=1'; + + return $app->redirect($url); + } catch (\Exception $e) { + return $app->redirect("/login/?redirect=" . $request->get('redirect') . "&error=" . _('An error occured')); + } + + if ($app['browser']->isMobile()) { + return $app->redirect("/lightbox/"); + } elseif ($request->get('redirect')) { + return $app->redirect($request->get('redirect')); + } elseif (true !== $app['browser']->isNewGeneration()) { + return $app->redirect('/client/'); + } else { + return $app->redirect('/prod/'); + } + } else { + return $app->redirect("/login/"); + } + } + /** * Prefix the method to call with the controller class name * @@ -772,7 +886,7 @@ class Login implements ControllerProviderInterface /** * Get required fields configuration * - * @param \Alchemy\Phrasea\Core $core + * @param Core $core * @return boolean */ private function getRegisterFieldConfiguration(Core $core) @@ -799,14 +913,13 @@ class Login implements ControllerProviderInterface "demand" => true ); - //on va chercher le fichier de configuration $registerFieldConfigurationFile = $core['Registry']->get('GV_RootPath') . 'config/register-fields.php'; if (is_file($registerFieldConfigurationFile)) { include $registerFieldConfigurationFile; } - //on force les champs vraiment obligatoires si le mec a fumé en faisant sa conf + //Override mandatory fields $arrayVerif['form_login'] = true; $arrayVerif['form_password'] = true; $arrayVerif['form_password_confirm'] = true; diff --git a/lib/Alchemy/Phrasea/Helper/User/Manage.php b/lib/Alchemy/Phrasea/Helper/User/Manage.php index 7d0d6f2a4c..2c2c4eac78 100644 --- a/lib/Alchemy/Phrasea/Helper/User/Manage.php +++ b/lib/Alchemy/Phrasea/Helper/User/Manage.php @@ -180,7 +180,7 @@ class Manage extends Helper $registry = \bootstrap::getCore()->getRegistry(); if (false !== $urlToken) { - $url = sprintf('%slogin/forgotpwd.php?token=%s', $registry->get('GV_ServerName'), $urlToken); + $url = sprintf('%slogin/forgot-password/?token=%s', $registry->get('GV_ServerName'), $urlToken); \mail::send_credentials($url, $createdUser->get_login(), $createdUser->get_email()); } } diff --git a/lib/Alchemy/Phrasea/Security/Firewall.php b/lib/Alchemy/Phrasea/Security/Firewall.php index e8d50592ce..f73c23641a 100644 --- a/lib/Alchemy/Phrasea/Security/Firewall.php +++ b/lib/Alchemy/Phrasea/Security/Firewall.php @@ -34,11 +34,4 @@ class Firewall return $app->redirect('/login/logout/'); } } - - public function requireNotAuthenticated(Application $app) - { - if ($app['phraseanet.core']->isAuthenticated()) { - return $app->redirect('/prod/'); - } - } } diff --git a/lib/classes/Exception/ServiceUnavalaible.class.php b/lib/classes/Exception/ServiceUnavailable.class.php similarity index 100% rename from lib/classes/Exception/ServiceUnavalaible.class.php rename to lib/classes/Exception/ServiceUnavailable.class.php diff --git a/lib/classes/eventsmanager/notify/autoregister.class.php b/lib/classes/eventsmanager/notify/autoregister.class.php index 3d21253308..fce6569b85 100644 --- a/lib/classes/eventsmanager/notify/autoregister.class.php +++ b/lib/classes/eventsmanager/notify/autoregister.class.php @@ -246,7 +246,7 @@ class eventsmanager_notify_autoregister extends eventsmanager_notifyAbstract $body .= "\n"; - $body .= "
\n
" + $body .= "
\n
" . _('admin::register: vous pourrez consulter son compte en ligne via l\'interface d\'administration') . "
\n"; diff --git a/lib/classes/eventsmanager/notify/register.class.php b/lib/classes/eventsmanager/notify/register.class.php index da648dd8aa..487bf896fe 100644 --- a/lib/classes/eventsmanager/notify/register.class.php +++ b/lib/classes/eventsmanager/notify/register.class.php @@ -250,7 +250,7 @@ class eventsmanager_notify_register extends eventsmanager_notifyAbstract $body .= "\n"; $body .= "
\n
" + . "login/?redirect=admin' target='_blank'>" . _('admin::register: vous pourrez traiter ses demandes en ligne via l\'interface d\'administration') . "
\n"; diff --git a/templates/web/account/reset-email.html.twig b/templates/web/account/reset-email.html.twig index 6c2a17cfd0..4428ebf7b4 100644 --- a/templates/web/account/reset-email.html.twig +++ b/templates/web/account/reset-email.html.twig @@ -48,8 +48,10 @@ $(document).ready(function() { {% block content %} {% if updateMsg is not none %} -
{{ updateMsg }}
- {% trans 'admin::compte-utilisateur retour a mon compte'%} +
+
{{ updateMsg }}
+ {% trans 'admin::compte-utilisateur retour a mon compte'%} +
{% else %} {% if noticeMsg is not none %} diff --git a/templates/web/account/reset-password.html.twig b/templates/web/account/reset-password.html.twig index 1b7b1b9152..0fadd145ec 100644 --- a/templates/web/account/reset-password.html.twig +++ b/templates/web/account/reset-password.html.twig @@ -56,7 +56,10 @@ {% block content %}
{% if passwordMsg is not none %} -

{{ passwordMsg }}

+
+ × + {{ passwordMsg }} +
{% endif %}
diff --git a/templates/web/login/forgot-password.html.twig b/templates/web/login/forgot-password.html.twig index 4ffde1c0f3..f840c5a4f5 100644 --- a/templates/web/login/forgot-password.html.twig +++ b/templates/web/login/forgot-password.html.twig @@ -124,7 +124,10 @@ {% if not tokenize %} {% if errorMsg is not none %} -
{{ errorMsg }}
+
+ × + {{ errorMsg }} +
{% endif %} {% if sentMsg is not none %} diff --git a/tests/Alchemy/Phrasea/Controller/Root/AccountTest.php b/tests/Alchemy/Phrasea/Controller/Root/AccountTest.php index d9db3f012d..27a894cf99 100644 --- a/tests/Alchemy/Phrasea/Controller/Root/AccountTest.php +++ b/tests/Alchemy/Phrasea/Controller/Root/AccountTest.php @@ -19,11 +19,11 @@ class AccountTest extends \PhraseanetWebTestCaseAuthenticatedAbstract public static function tearDownAfterClass() { - parent::tearDownAfterClass(); - if (self::$authorizedApp) { self::$authorizedApp->delete(); } + + parent::tearDownAfterClass(); } public function setUp() @@ -246,7 +246,7 @@ class AccountTest extends \PhraseanetWebTestCaseAuthenticatedAbstract $this->assertTrue($this->client->getResponse()->isOk()); - $this->assertEquals(1, $crawler->filter('.update-msg')->count()); + $this->assertEquals(1, $crawler->filter('.alert-info')->count()); } public function updateMsgProvider() @@ -307,7 +307,7 @@ class AccountTest extends \PhraseanetWebTestCaseAuthenticatedAbstract $this->assertTrue($response->isOk()); - $this->assertEquals(1, $crawler->filter('.password-msg')->count()); + $this->assertEquals(1, $crawler->filter('.alert-error')->count()); } public function passwordMsgProvider() diff --git a/tests/Alchemy/Phrasea/Controller/Root/DevelopersTest.php b/tests/Alchemy/Phrasea/Controller/Root/DevelopersTest.php index 9f3668d828..3dcabbe370 100644 --- a/tests/Alchemy/Phrasea/Controller/Root/DevelopersTest.php +++ b/tests/Alchemy/Phrasea/Controller/Root/DevelopersTest.php @@ -127,10 +127,7 @@ class DevelopersTest extends \PhraseanetWebTestCaseAuthenticatedAbstract */ public function testDeleteAppError() { - $this->client->request('DELETE', '/developers/application/0/', array(), array(), array( - 'HTTP_ACCEPT' => 'application/json', - 'HTTP_X-Requested-With' => 'XMLHttpRequest' - )); + $this->XMLHTTPRequest('DELETE', '/developers/application/0/'); $this->assertTrue($this->client->getResponse()->isOk()); $content = json_decode($this->client->getResponse()->getContent()); @@ -144,11 +141,7 @@ class DevelopersTest extends \PhraseanetWebTestCaseAuthenticatedAbstract { $oauthApp = \API_OAuth2_Application::create(\appbox::get_instance(\bootstrap::getCore()), self::$user, 'test app'); - $this->client->request('DELETE', '/developers/application/' . $oauthApp->get_id() . '/', array(), array(), array( - 'HTTP_ACCEPT' => 'application/json', - 'HTTP_X-Requested-With' => 'XMLHttpRequest' - ) - ); + $this->XMLHTTPRequest('DELETE', '/developers/application/' . $oauthApp->get_id() . '/'); $this->assertTrue($this->client->getResponse()->isOk()); @@ -174,12 +167,9 @@ class DevelopersTest extends \PhraseanetWebTestCaseAuthenticatedAbstract */ public function testRenewAppCallbackError() { - $this->client->request('POST', '/developers/application/0/callback/', array( + $this->XMLHTTPRequest('POST', '/developers/application/0/callback/', array( 'callback' => 'my.callback.com' - ), array(), array( - 'HTTP_ACCEPT' => 'application/json', - 'HTTP_X-Requested-With' => 'XMLHttpRequest', - )); + )); $this->assertTrue($this->client->getResponse()->isOk()); $content = json_decode($this->client->getResponse()->getContent()); @@ -193,12 +183,7 @@ class DevelopersTest extends \PhraseanetWebTestCaseAuthenticatedAbstract { $oauthApp = \API_OAuth2_Application::create(\appbox::get_instance(\bootstrap::getCore()), self::$user, 'test app'); - $this->client->request('POST', '/developers/application/'.$oauthApp->get_id().'/callback/', array( - 'callback' => null - ), array(), array( - 'HTTP_ACCEPT' => 'application/json', - 'HTTP_X-Requested-With' => 'XMLHttpRequest', - )); + $this->XMLHTTPRequest('POST', '/developers/application/'.$oauthApp->get_id().'/callback/'); $this->assertTrue($this->client->getResponse()->isOk()); $content = json_decode($this->client->getResponse()->getContent()); @@ -212,12 +197,9 @@ class DevelopersTest extends \PhraseanetWebTestCaseAuthenticatedAbstract { $oauthApp = \API_OAuth2_Application::create(\appbox::get_instance(\bootstrap::getCore()), self::$user, 'test app'); - $this->client->request('POST', '/developers/application/' . $oauthApp->get_id() . '/callback/', array( + $this->XMLHTTPRequest('POST', '/developers/application/' . $oauthApp->get_id() . '/callback/', array( 'callback' => 'my.callback.com' - ), array(), array( - 'HTTP_ACCEPT' => 'application/json', - 'HTTP_X-Requested-With' => 'XMLHttpRequest', - )); + )); $this->assertTrue($this->client->getResponse()->isOk()); $content = json_decode($this->client->getResponse()->getContent()); @@ -240,12 +222,9 @@ class DevelopersTest extends \PhraseanetWebTestCaseAuthenticatedAbstract */ public function testRenewAccessTokenError() { - $this->client->request('POST', '/developers/application/0/access_token/', array( + $this->XMLHTTPRequest('POST', '/developers/application/0/access_token/', array( 'callback' => 'my.callback.com' - ), array(), array( - 'HTTP_ACCEPT' => 'application/json', - 'HTTP_X-Requested-With' => 'XMLHttpRequest', - )); + )); $this->assertTrue($this->client->getResponse()->isOk()); $content = json_decode($this->client->getResponse()->getContent()); @@ -260,10 +239,7 @@ class DevelopersTest extends \PhraseanetWebTestCaseAuthenticatedAbstract { $oauthApp = \API_OAuth2_Application::create(\appbox::get_instance(\bootstrap::getCore()), self::$user, 'test app'); - $this->client->request('POST', '/developers/application/' . $oauthApp->get_id() . '/access_token/', array(), array(), array( - 'HTTP_ACCEPT' => 'application/json', - 'HTTP_X-Requested-With' => 'XMLHttpRequest', - )); + $this->XMLHTTPRequest('POST', '/developers/application/' . $oauthApp->get_id() . '/access_token/'); $this->assertTrue($this->client->getResponse()->isOk()); $content = json_decode($this->client->getResponse()->getContent()); @@ -285,12 +261,9 @@ class DevelopersTest extends \PhraseanetWebTestCaseAuthenticatedAbstract */ public function testAuthorizeGrantpasswordError() { - $this->client->request('POST', '/developers/application/0/authorize_grant_password/', array( + $this->XMLHTTPRequest('POST', '/developers/application/0/authorize_grant_password/', array( 'callback' => 'my.callback.com' - ), array(), array( - 'HTTP_ACCEPT' => 'application/json', - 'HTTP_X-Requested-With' => 'XMLHttpRequest', - )); + )); $this->assertTrue($this->client->getResponse()->isOk()); $content = json_decode($this->client->getResponse()->getContent()); @@ -304,11 +277,8 @@ class DevelopersTest extends \PhraseanetWebTestCaseAuthenticatedAbstract { $oauthApp = \API_OAuth2_Application::create(\appbox::get_instance(\bootstrap::getCore()), self::$user, 'test app'); - $this->client->request('POST', '/developers/application/' . $oauthApp->get_id() . '/authorize_grant_password/', array( + $this->XMLHTTPRequest('POST', '/developers/application/' . $oauthApp->get_id() . '/authorize_grant_password/', array( 'grant' => '1' - ), array(), array( - 'HTTP_ACCEPT' => 'application/json', - 'HTTP_X-Requested-With' => 'XMLHttpRequest', )); $this->assertTrue($this->client->getResponse()->isOk()); diff --git a/tests/Alchemy/Phrasea/Controller/Root/LoginTest.php b/tests/Alchemy/Phrasea/Controller/Root/LoginTest.php index 92db5a7763..c7d7884deb 100644 --- a/tests/Alchemy/Phrasea/Controller/Root/LoginTest.php +++ b/tests/Alchemy/Phrasea/Controller/Root/LoginTest.php @@ -43,7 +43,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract $this->client->request('GET', '/login/', array('postlog' => '1', 'redirect' => 'prod')); $response = $this->client->getResponse(); $this->assertTrue($response->isRedirect()); - $this->assertEquals('/login/?redirect=prod', $response->headers->get('location')); + $this->assertEquals('/login/logout/?redirect=prod', $response->headers->get('location')); } /** @@ -105,7 +105,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract */ public function testRegisterConfirmMailUserNotFound() { - $email = \random::generatePassword() . '_email@email.com'; + $email = $this->generateEmail(); $token = \random::getUrlToken(\random::TYPE_EMAIL, 0, null, $email); $this->client->request('GET', '/login/register-confirm/', array('code' => $token)); $response = $this->client->getResponse(); @@ -119,7 +119,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract */ public function testRegisterConfirmMailUnlocked() { - $email = \random::generatePassword() . '_email@email.com'; + $email = $this->generateEmail(); $token = \random::getUrlToken(\random::TYPE_EMAIL, self::$user->get_id(), null, $email); self::$user->set_mail_locked(false); @@ -136,7 +136,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract */ public function testRegisterConfirmMail() { - $email = \random::generatePassword() . '_email@email.com'; + $email = $this->generateEmail(); $appboxRegister = new \appbox_register($this->app['phraseanet.appbox']); $token = \random::getUrlToken(\random::TYPE_EMAIL, self::$user->get_id(), null, $email); @@ -156,7 +156,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract */ public function testRegisterConfirmMailNoCollAwait() { - $email = \random::generatePassword() . '_email@email.com'; + $email = $this->generateEmail(); $token = \random::getUrlToken(\random::TYPE_EMAIL, self::$user->get_id(), null, $email); self::$user->set_mail_locked(true); @@ -290,7 +290,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract )); $this->assertTrue($this->client->getResponse()->isOk()); - $this->assertEquals(1, $crawler->filter('.error-msg')->count()); + $this->assertEquals(1, $crawler->filter('.alert-error')->count()); } /** @@ -305,7 +305,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract $response = $this->client->getResponse(); $this->assertTrue($response->isOk()); - $this->assertEquals(1, $crawler->filter('.error-msg')->count()); + $this->assertEquals(1, $crawler->filter('.alert-error')->count()); } public function errorMessageProvider() @@ -357,7 +357,7 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract /** * @todo change this */ - if(\login::register_enabled()) { + if ( ! \login::register_enabled()) { $this->assertTrue($this->client->getResponse()->isRedirect()); } else { $this->assertTrue($this->client->getResponse()->isOk()); @@ -641,6 +641,81 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract $this->assertEquals('/login/?error=user-not-found', $response->headers->get('location')); } + /** + * @covers \Alchemy\Phrasea\Controller\Root\Login::authenticate + */ + public function testAuthenticate() + { + $this->app['phraseanet.appbox']->get_session()->logout(); + $password = \random::generatePassword(); + self::$user->set_password($password); + $this->client->request('POST', '/login/authenticate/', array( + 'login' => self::$user->get_login(), + 'pwd' => $password + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + $this->assertTrue($this->app['phraseanet.core']->isAuthenticated()); + } + + /** + * @covers \Alchemy\Phrasea\Controller\Root\Login::authenticate + */ + public function testBadAuthenticate() + { + $this->app['phraseanet.appbox']->get_session()->logout(); + $this->client->request('POST', '/login/authenticate/', array( + 'login' => self::$user->get_login(), + 'pwd' => 'test' + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + $this->assertRegexp('/error=auth/', $this->client->getResponse()->headers->get('location')); + $this->assertFalse($this->app['phraseanet.core']->isAuthenticated()); + } + + /** + * @covers \Alchemy\Phrasea\Controller\Root\Login::authenticate + */ + public function testMailLockedAuthenticate() + { + $this->app['phraseanet.appbox']->get_session()->logout(); + $password = \random::generatePassword(); + self::$user->set_mail_locked(true); + $this->client->request('POST', '/login/authenticate/', array( + 'login' => self::$user->get_login(), + 'pwd' => $password + )); + + $this->assertTrue($this->client->getResponse()->isRedirect()); + $this->assertRegexp('/error=mail-not-confirmed/', $this->client->getResponse()->headers->get('location')); + $this->assertFalse($this->app['phraseanet.core']->isAuthenticated()); + self::$user->set_mail_locked(false); + } + + /** + * @covers \Alchemy\Phrasea\Controller\Root\Login::authenticate + */ + public function testAuthenticateUnavailable() + { + $this->app['phraseanet.appbox']->get_session()->logout(); + $password = \random::generatePassword(); + $this->app['phraseanet.core']['Registry']->set('GV_maintenance', true , \registry::TYPE_BOOLEAN); + $this->client->request('POST', '/login/authenticate/', array( + 'login' => self::$user->get_login(), + 'pwd' => $password + )); + $this->app['phraseanet.core']['Registry']->set('GV_maintenance', false, \registry::TYPE_BOOLEAN); + $this->assertTrue($this->client->getResponse()->isRedirect()); + $this->assertRegexp('/error=maintenance/', $this->client->getResponse()->headers->get('location')); + $this->assertFalse($this->app['phraseanet.core']->isAuthenticated()); + + } + + /** + * Delete inscription demand made by the current authenticathed user + * @return void + */ private function deleteRequest() { $sql = "DELETE FROM demand WHERE usr_id = :usr_id"; @@ -648,4 +723,13 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract $stmt->execute(array(':usr_id' => self::$user->get_id())); $stmt->closeCursor(); } + + /** + * Generate a new valid email adress + * @return string + */ + private function generateEmail() + { + return \random::generatePassword() . '_email@email.com'; + } } diff --git a/tests/PhraseanetPHPUnitAbstract.class.inc b/tests/PhraseanetPHPUnitAbstract.class.inc index f56312bd62..e6f899fa23 100644 --- a/tests/PhraseanetPHPUnitAbstract.class.inc +++ b/tests/PhraseanetPHPUnitAbstract.class.inc @@ -4,6 +4,8 @@ require_once __DIR__ . "/PhraseanetPHPUnitListener.class.inc"; use Silex\WebTestCase; use Doctrine\Common\DataFixtures\Loader; +use Symfony\Component\HttpKernel\Client; +use Symfony\Component\DomCrawler\Crawler; abstract class PhraseanetPHPUnitAbstract extends WebTestCase { @@ -485,6 +487,28 @@ abstract class PhraseanetPHPUnitAbstract extends WebTestCase return; } + /** + * Calls a URI as XMLHTTP request. + * + * @param string $method The request method + * @param string $uri The URI to fetch + * @param array $parameters The Request parameters + * @param array $httpAccept Contents of the Accept header + * + * @return Crawler + */ + protected function XMLHTTPRequest($method, $uri, array $parameters = array(), $httpAccept = 'application/json') + { + if ( ! $this->client instanceof Client) { + throw new \Exception('Not client seems intitialized'); + } + + return $this->client->request($method, $uri, $parameters, array(), array( + 'HTTP_ACCEPT' => $httpAccept, + 'HTTP_X-Requested-With' => 'XMLHttpRequest', + )); + } + /** * Update the sql tables with the current schema * @return void @@ -608,6 +632,7 @@ abstract class PhraseanetPHPUnitAbstract extends WebTestCase foreach ($databox->get_collections() as $collection) { $base_id = $collection->get_base_id(); + $user->ACL()->give_access_to_base(array($base_id)); set_exportorder::set_order_admins(array($user->get_id()), $base_id); diff --git a/www/include/jquery.common.js b/www/include/jquery.common.js index b32ec5d06f..4bccea56c9 100644 --- a/www/include/jquery.common.js +++ b/www/include/jquery.common.js @@ -87,7 +87,7 @@ function login(what) { EcrireCookie('last_act',what,null,'/'); } - self.location.replace('/login/index.php?postlog=1'); + self.location.replace('/login/?postlog=1'); } return false; }