diff --git a/config/nginx.rewrite.rules b/config/nginx.rewrite.rules index 5e54b68b17..069105ae8b 100644 --- a/config/nginx.rewrite.rules +++ b/config/nginx.rewrite.rules @@ -45,6 +45,8 @@ rewrite ^/prod/notifications/.*$ /prod/router.php last; rewrite ^/robots.txt$ /index.php last; rewrite ^/feeds/.*$ /index.php last; +rewrite ^/account/.*$ /index.php last; +rewrite ^/developers/.*$ /index.php last; rewrite ^/lightbox/.*$ /lightbox/index.php last; rewrite ^/api/v1/.*$ /api/v1/index.php last; diff --git a/lib/Alchemy/Phrasea/Application/OAuth2.php b/lib/Alchemy/Phrasea/Application/OAuth2.php index 32ad573ae3..9ab1734710 100644 --- a/lib/Alchemy/Phrasea/Application/OAuth2.php +++ b/lib/Alchemy/Phrasea/Application/OAuth2.php @@ -201,207 +201,6 @@ return; }); - /****************************************************************** - * MANAGEMENT APPS - * - * - */ - /** - * list of all authorized apps by logged user - */ - $route = '/applications'; - $app->get($route, function() use ($app) { - $apps = \API_OAuth2_Application::load_app_by_user($app['appbox'], $app['Core']->getAuthenticatedUser()); - - return $app['response']('api/auth/applications.twig', array("apps" => $apps, 'user' => $app['Core']->getAuthenticatedUser())); - }); - - /** - * list of apps created by user - */ - $route = "/applications/dev"; - $app->get($route, function() use ($app) { - $rs = \API_OAuth2_Application::load_dev_app_by_user($app['appbox'], $app['Core']->getAuthenticatedUser()); - - return $app['response']('api/auth/application_dev.twig', array("apps" => $rs)); - }); - - /** - * display a new app form - */ - $route = "/applications/dev/new"; - $app->get($route, function() use ($app) { - $var = array("violations" => null, 'form' => null, 'request' => $app['request']); - - return $app['response']('api/auth/application_dev_new.twig', $var); - }); - - $route = "/applications/dev/create"; - $app->post($route, function() use ($app) { - $submit = false; - if ($app['request']->get("type") == "desktop") { - $post = new \API_OAuth2_Form_DevAppDesktop($app['request']); - } else { - $post = new \API_OAuth2_Form_DevAppInternet($app['request']); - } - - $violations = $app['validator']->validate($post); - - if ($violations->count() == 0) - $submit = true; - - $request = $app['request']; - - if ($submit) { - $application = \API_OAuth2_Application::create($app['appbox'], $app['Core']->getAuthenticatedUser(), $post->getName()); - $application->set_description($post->getDescription()) - ->set_redirect_uri($post->getSchemeCallback() . $post->getCallback()) - ->set_type($post->getType()) - ->set_website($post->getSchemeWebsite() . $post->getWebsite()); - - return $app->redirect("/api/oauthv2/applications/dev/" . $application->get_id() . "/show"); - } - - $var = array( - "violations" => $violations, - "form" => $post - ); - - return $app['response']('api/auth/application_dev_new.twig', $var); - }); - - /** - * show details of app identified by its id - */ - $route = "/applications/dev/{id}/show"; - $app->get($route, function($id) use ($app) { - $client = new \API_OAuth2_Application($app['appbox'], $id); - $token = $client->get_user_account($app['Core']->getAuthenticatedUser())->get_token()->get_value(); - $var = array("app" => $client, "user" => $app['Core']->getAuthenticatedUser(), "token" => $token); - - return $app['response']('api/auth/application_dev_show.twig', $var); - })->assert('id', '\d+'); - - /** - * revoke access from a user to the app - * identified by account id - */ - $route = "/applications/revoke_access/"; - $app->post($route, function() use ($app) { - $result = array("ok" => false); - try { - $account = new \API_OAuth2_Account($app['appbox'], $app['request']->get('account_id')); - $account->set_revoked((bool) $app['request']->get('revoke')); - $result['ok'] = true; - } catch (\Exception $e) { - - } - - $Serializer = $app['Core']['Serializer']; - - return new Response( - $Serializer->serialize($result, 'json') - , 200 - , array("content-type" => "application/json") - ); - }); - - /** - * revoke access from a user to the app - * identified by account id - */ - $route = "/applications/{appId}/grant_password/"; - $app->post($route, function($appId) use ($app) { - $result = array("ok" => false); - try { - $client = new \API_OAuth2_Application($app['appbox'], $appId); - $client->set_grant_password((bool) $app['request']->get('grant')); - $result['ok'] = true; - } catch (\Exception $e) { - - } - - $Serializer = $app['Core']['Serializer']; - - return new Response( - $Serializer->serialize($result, 'json') - , 200 - , array("content-type" => "application/json") - ); - }); - - $route = "/applications/{id}/generate_access_token/"; - $app->post($route, function($id) use ($app) { - $result = array("ok" => false); - try { - $client = new \API_OAuth2_Application($app['appbox'], $id); - $account = $client->get_user_account($app['Core']->getAuthenticatedUser()); - - $token = $account->get_token(); - - if ($token instanceof API_OAuth2_Token) - $token->renew(); - else - $token = \API_OAuth2_Token::create($app['appbox'], $account); - - $result = array( - "ok" => true - , 'token' => $token->get_value() - ); - } catch (\Exception $e) { - - } - - $Serializer = $app['Core']['Serializer']; - - return new Response( - $Serializer->serialize($result, 'json') - , 200 - , array("content-type" => "application/json") - ); - })->assert('id', '\d+'); - - $route = "/applications/oauth_callback"; - $app->post($route, function() use ($app) { - $app_id = $app['request']->request->get("app_id"); - $app_callback = $app["request"]->request->get("callback"); - $result = array("success" => false); - try { - $client = new \API_OAuth2_Application($app['appbox'], $app_id); - $client->set_redirect_uri($app_callback); - $result['success'] = true; - } catch (\Exception $e) { - - } - - $Serializer = $app['Core']['Serializer']; - - return new Response( - $Serializer->serialize($result, 'json') - , 200 - , array("content-type" => "application/json") - ); - }); - - $route = "/applications/{id}"; - $app->delete($route, function($id) use ($app) { - $result = array("success" => false); - try { - $client = new \API_OAuth2_Application($app['appbox'], $id); - $client->delete(); - $result['success'] = true; - } catch (\Exception $e) { - - } - - $Serializer = $app['Core']['Serializer']; - - return new Response( - $Serializer->serialize($result, 'json') - , 200 - , array("content-type" => "application/json") - ); - })->assert('id', '\d+'); /** * ******************************************************************* * diff --git a/lib/Alchemy/Phrasea/Application/Root.php b/lib/Alchemy/Phrasea/Application/Root.php index 4b47b35a74..5b8ed6f7ae 100644 --- a/lib/Alchemy/Phrasea/Application/Root.php +++ b/lib/Alchemy/Phrasea/Application/Root.php @@ -11,8 +11,10 @@ namespace Alchemy\Phrasea\Application; -use Symfony\Component\HttpFoundation\Response; use Alchemy\Phrasea\Controller\Root as Controller; +use Silex\Application as SilexApp; +use Silex\Provider\ValidatorServiceProvider; +use Symfony\Component\HttpFoundation\Response; /** * @@ -20,17 +22,17 @@ * @link www.phraseanet.com */ return call_user_func(function() { - $app = new \Silex\Application(); + $app = new SilexApp(); $app['Core'] = \bootstrap::getCore(); - if ( ! \setup::is_installed()) { - $response = new \Symfony\Component\HttpFoundation\RedirectResponse('/setup/'); + $app->register(new ValidatorServiceProvider()); - return $response->send(); - } + $app->before(function () use ($app) { + $app['Core']['Firewall']->requireSetup($app); + }); - $app->get('/', function() use ($app) { + $app->get('/', function(SilexApp $app) { $browser = \Browser::getInstance(); if ($browser->isMobile()) { return $app->redirect("/login/?redirect=/lightbox"); @@ -41,17 +43,12 @@ } }); - $app->get('/robots.txt', function() use ($app) { - $appbox = \appbox::get_instance($app['Core']); - - $registry = $appbox->get_registry(); + $app->get('/robots.txt', function(SilexApp $app) { - if ($registry->get('GV_allow_search_engine') === true) { - $buffer = "User-Agent: *\n" - . "Allow: /\n"; + if ($app['Core']['Registry']->get('GV_allow_search_engine') === true) { + $buffer = "User-Agent: *\n" . "Allow: /\n"; } else { - $buffer = "User-Agent: *\n" - . "Disallow: /\n"; + $buffer = "User-Agent: *\n" . "Disallow: /\n"; } $response = new Response($buffer, 200, array('Content-Type' => 'text/plain')); @@ -61,6 +58,8 @@ }); $app->mount('/feeds/', new Controller\RSSFeeds()); + $app->mount('/account/', new Controller\Account()); + $app->mount('/developers/', new Controller\Developers()); return $app; } diff --git a/lib/Alchemy/Phrasea/Controller/Root/Account.php b/lib/Alchemy/Phrasea/Controller/Root/Account.php new file mode 100644 index 0000000000..fd9c42b229 --- /dev/null +++ b/lib/Alchemy/Phrasea/Controller/Root/Account.php @@ -0,0 +1,582 @@ +before(function() use ($app) { + $app['Core']['Firewall']->requireAuthentication($app); + }); + + /** + * New account route + * + * name : get_account + * + * description : Display form to create a new account + * + * method : GET + * + * parameters : none + * + * return : HTML Response + */ + $controllers->get('/', $this->call('displayAccount')) + ->bind('get_account'); + + /** + * Create account route + * + * name : create_account + * + * description : update your account informations + * + * method : POST + * + * parameters : + * 'gender' + * 'lastname' + * 'firstname' + * 'job' + * 'lastname' + * 'company' + * 'function' + * 'activity' + * 'phone' + * 'fax' + * 'address' + * 'zip_code' + * 'geoname_id' + * 'dest_ftp' + * 'default_data_ftp' + * 'prefix_ftp_folder' + * 'notice' + * 'bases' + * 'mail_notifications' + * 'request_notifications' + * 'demand' + * 'notifications' + * 'active_ftp' + * 'address_ftp' + * 'login_ftp' + * 'password_ftp' + * 'pass_if_ftp' + * 'retry_ftp' + * + * + * return : HTML Response + */ + $controllers->post('/', $this->call('updateAccount')) + ->bind('create_account'); + + + /** + * Forgot password + * + * name : account_forgot_password + * + * description : Display form to renew password + * + * method : GET + * + * parameters : none + * + * return : HTML Response + */ + $controllers->get('/forgot-password/', $this->call('displayForgotPasswordForm')) + ->bind('account_forgot_password'); + + /** + * Renew password + * + * name : account_renew_password + * + * description : Register the new user password + * + * method : POST + * + * parameters : none + * + * return : HTML Response + */ + $controllers->post('/forgot-password/', $this->call('renewPassword')) + ->bind('post_account_forgot_password'); + + /** + * Give account access + * + * name : account_access + * + * description : Display form to create a new account + * + * method : GET + * + * parameters : none + * + * return : HTML Response + */ + $controllers->get('/access/', $this->call('accountAccess')) + ->bind('account_access'); + + /** + * Give account open sessions + * + * name : account_security_sessions + * + * description : Display form to create a new account + * + * method : GET + * + * parameters : none + * + * return : HTML Response + */ + $controllers->get('/security/sessions/', $this->call('accountSessionsAccess')) + ->bind('account_security_sessions'); + + /** + * Give authorized applications that can access user informations + * + * name : account_security_applications + * + * description : Display form to create a new account + * + * method : GET + * + * parameters : none + * + * return : HTML Response + */ + $controllers->get('/security/applications/', $this->call('accountAuthorizedApps')) + ->bind('account_security_applications'); + + /** + * Grant access to an authorized app + * + * name : account_security_applications_grant + * + * description : Display form to create a new account + * + * method : GET + * + * parameters : none + * + * return : HTML Response + */ + $controllers->get('/security/application/{application_id}/grant/', $this->call('grantAccess')) + ->assert('application_id', '\d+') + ->bind('account_security_applications_grant'); + + return $controllers; + } + + /** + * Submit the new password + * + * @param Application $app A Silex application where the controller is mounted on + * @param Request $request The current request + * @return Response + */ + public function renewPassword(Application $app, Request $request) + { + $appbox = \appbox::get_instance($app['Core']); + + // send mail + if ('' !== $mail = trim($request->get('mail', ''))) { + if ( ! \PHPMailer::ValidateAddress($mail)) { + return $app->redirect('/account/forgot-password/?error=invalidmail'); + } + + try { + $user = \User_Adapter::getInstance(\User_Adapter::get_usr_id_from_email($mail), $appbox); + } catch (\Exception $e) { + return $app->redirect('/account/forgot-password/?error=noaccount'); + } + + $token = \random::getUrlToken(\random::TYPE_PASSWORD, $user->get_id(), new \DateTime('+1 day')); + + if ($token) { + $url = sprintf('%saccount/forgot-password/?token=%s', $app['Registry']->get('GV_ServerName'), $token); + + if (\mail::forgot_passord($email, $user->get_login(), $url)) { + return $app->redirect('/account/forgot-password/?sent=ok'); + } else { + return $app->redirect('/account/forgot-password/?error=mailserver'); + } + } + + return $app->redirect('/account/forgot-password/?error=noaccount'); + } + + if (null !== $token = $request->get('token') + && null !== $password = $request->get('form_password') + && null !== $passwordConfirm = $request->get('form_password_confirm')) { + + if ($password !== $passwordConfirm) { + + return $app->redirect('/account/forgot-password/?pass-error=pass-match'); + } elseif (strlen(trim($password)) < 5) { + + return $app->redirect('/account/forgot-password/?pass-error=pass-short'); + } elseif (trim($password) != str_replace(array("\r\n", "\n", "\r", "\t", " "), "_", $password)) { + + return $app->redirect('/account/forgot-password/?pass-error=pass-invalid'); + } + + try { + $datas = \random::helloToken($token); + + $user = \User_Adapter::getInstance($datas['usr_id'], $appbox); + $user->set_password($passwordConfirm); + + \random::removeToken($token); + + return $app->redirect('/login/?confirm=password-update-ok'); + } catch (\Exception_NotFound $e) { + + } + } + } + + /** + * 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 + */ + public function displayForgotPasswordForm(Application $app, Request $request) + { + $tokenize = false; + $errorMsg = $request->get('error'); + + if (null !== $token = $request->get('token')) { + try { + \random::helloToken($token); + $tokenize = true; + } catch (\Exception $e) { + $errorMsg = 'token'; + } + } + + if (null !== $errorMsg) { + switch ($errorMsg) { + case 'invalidmail': + $errorMsg = _('Invalid email address'); + break; + case 'mailserver': + $errorMsg = _('phraseanet::erreur: Echec du serveur mail'); + break; + case 'noaccount': + $errorMsg = _('phraseanet::erreur: Le compte n\'a pas ete trouve'); + break; + case 'mail': + $errorMsg = _('phraseanet::erreur: Echec du serveur mail'); + break; + case 'token': + $errorMsg = _('phraseanet::erreur: l\'url n\'est plus valide'); + break; + } + } + + if (null !== $sentMsg = $request->get('sent')) { + switch ($sentMsg) { + case 'ok': + $sentMsg = _('phraseanet:: Un email vient de vous etre envoye'); + break; + } + } + + if (null !== $passwordMsg = $request->get('pass-error')) { + switch ($sentMsg) { + case 'pass-match': + $sentMsg = _('forms::les mots de passe ne correspondent pas'); + break; + case 'pass-short': + $sentMsg = _('forms::la valeur donnee est trop courte'); + break; + case 'pass-invalid': + $sentMsg = _('forms::la valeur donnee contient des caracteres invalides'); + break; + } + } + + return new Response($app['Core']['Twig']->render('account/forgot-password.html.twig', array( + 'needed' => array(), + 'tokenize' => $tokenize, + 'passwordMsg' => $passwordMsg, + 'errorMsg' => $errorMsg, + 'sentMsg' => $sentMsg + ))); + } + + /** + * 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 + */ + public function grantAccess(Application $app, Request $request, $application_id) + { + if ( ! $request->isXmlHttpRequest() || ! array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) { + $app->abort(400, _('Bad request format, only JSON is allowed')); + } + + $appbox = \appbox::get_instance($app['Core']); + $error = false; + + try { + $account = \API_OAuth2_Account::load_with_user( + $appbox + , new \API_OAuth2_Application($appbox, $application_id) + , $app['Core']->getAuthenticatedUser() + ); + } catch (\Exception_NotFound $e) { + $error = true; + } + + $account->set_revoked((bool) $request->get('revoke'), false); + + return new JsonResponse(array('success' => ! $error)); + } + + /** + * 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 + */ + public function accountAuthorizedApps(Application $app, Request $request) + { + $user = $app['Core']->getAuthenticatedUser(); + + return $app['Core']['Twig']->render('account/authorized_apps.html.twig', array( + "apps" => \API_OAuth2_Application::load_app_by_user(\appbox::get_instance($app['Core']), $user), + 'user' => $user + )); + } + + /** + * Display account session accesss + * + * @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) + { + return new Response($app['Core']['Twig']->render('account/sessions.html.twig')); + } + + /** + * Display account base access + * + * @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) + { + require_once $app['Core']['Registry']->get('GV_RootPath') . 'lib/classes/deprecated/inscript.api.php'; + + return new Response($app['Core']['Twig']->render('account/access.html.twig', array( + 'inscriptions' => giveMeBases($app['Core']->getAuthenticatedUser()->get_id()) + ))); + } + + /** + * Display account form + * + * @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) + { + $appbox = \appbox::get_instance($app['Core']); + $user = $app['Core']->getAuthenticatedUser(); + $evtMngr = \eventsmanager_broker::getInstance($appbox, $app['Core']); + + switch ($notice = $request->get('notice', '')) { + case 'password-update-ok': + $notice = _('login::notification: Mise a jour du mot de passe avec succes'); + break; + case 'account-update-ok': + $notice = _('login::notification: Changements enregistres'); + break; + case 'account-update-bad': + $notice = _('forms::erreurs lors de l\'enregistrement des modifications'); + break; + case 'demand-ok': + $notice = _('login::notification: Vos demandes ont ete prises en compte'); + break; + } + + return new Response($app['Core']['Twig']->render('account/account.html.twig', array( + 'geonames' => new \geonames(), + 'user' => $user, + 'notice' => $notice, + 'evt_mngr' => $evtMngr, + 'notifications' => $evtMngr->list_notifications_available($user->get_id()), + ))); + } + + /** + * Update account informations + * + * @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) + { + $appbox = \appbox::get_instance($app['Core']); + $user = $app['Core']->getAuthenticatedUser(); + $evtMngr = \eventsmanager_broker::getInstance($appbox, $app['Core']); + $notice = 'account-update-bad'; + + $demands = (array) $request->get('demand', array()); + + if (0 === count($demands)) { + $register = new \appbox_register($appbox); + + foreach ($demands as $baseId) { + try { + $register->add_request($user, \collection::get_from_base_id($baseId)); + $notice = 'demand-ok'; + } catch (\Exception $e) { + + } + } + } + + $accountFields = array( + 'form_gender', + 'form_firstname', + 'form_lastname', + 'form_address', + 'form_zip', + 'form_phone', + 'form_fax', + 'form_function', + 'form_company', + 'form_activity', + 'form_geonameid', + 'form_addrFTP', + 'form_loginFTP', + 'form_pwdFTP', + 'form_destFTP', + 'form_prefixFTPfolder' + ); + + if (0 === count(array_diff($accountFields, array_keys($request->request->all())))) { + $defaultDatas = 0; + + if ($datas = (array) $request->get("form_defaultdataFTP", array())) { + if (in_array('document', $datas)) { + $defaultDatas += 4; + } + + if (in_array('preview', $datas)) { + $defaultDatas += 2; + } + + if (in_array('caption', $datas)) { + $defaultDatas += 1; + } + } + + try { + $appbox->get_connection()->beginTransaction(); + + $user->set_gender($request->get("form_gender")) + ->set_firstname($request->get("form_firstname")) + ->set_lastname($request->get("form_lastname")) + ->set_address($request->get("form_address")) + ->set_zip($request->get("form_zip")) + ->set_tel($request->get("form_phone")) + ->set_fax($request->get("form_fax")) + ->set_job($request->get("form_activity")) + ->set_company($request->get("form_company")) + ->set_position($request->get("form_function")) + ->set_geonameid($request->get("form_geonameid")) + ->set_mail_notifications((bool) $request->get("mail_notifications")) + ->set_activeftp($request->get("form_activeFTP")) + ->set_ftp_address($request->get("form_addrFTP")) + ->set_ftp_login($request->get("form_loginFTP")) + ->set_ftp_password($request->get("form_pwdFTP")) + ->set_ftp_passif($request->get("form_passifFTP")) + ->set_ftp_dir($request->get("form_destFTP")) + ->set_ftp_dir_prefix($request->get("form_prefixFTPfolder")) + ->set_defaultftpdatas($defaultDatas); + + $appbox->get_connection()->commit(); + + $notice = 'account-update-ok'; + } catch (Exception $e) { + $appbox->get_connection()->rollBack(); + } + } + + $requestedNotifications = (array) $request->get('notifications', array()); + + foreach ($evtMngr->list_notifications_available($user->get_id()) as $notifications) { + foreach ($notifications as $notification) { + $notifId = (int) $notification['id']; + $notifName = sprintf('notification_%d', $notifId); + + if (isset($requestedNotifications[$notifId])) { + $user->setPrefs($notifName, '1'); + } else { + $user->setPrefs($notifName, '0'); + } + } + } + + return $app->redirect(sprintf('/account/?notice=%s', $notice), 201); + } + + /** + * Prefix the method to call with the controller class name + * + * @param string $method The method to call + * @return string + */ + private function call($method) + { + return sprintf('%s::%s', __CLASS__, $method); + } +} diff --git a/lib/Alchemy/Phrasea/Controller/Root/Developers.php b/lib/Alchemy/Phrasea/Controller/Root/Developers.php new file mode 100644 index 0000000000..1b4c9e5c58 --- /dev/null +++ b/lib/Alchemy/Phrasea/Controller/Root/Developers.php @@ -0,0 +1,416 @@ +before(function() use ($app) { + $app['Core']['Firewall']->requireAuthentication($app); + }); + + /** + * List of apps created by the user + * + * name : developers_applications + * + * description : List all user applications + * + * method : GET + * + * parameters : none + * + * return : HTML Response + */ + $controllers->get('/applications/', $this->call('listApps')) + ->bind('developers_applications'); + + + + /** + * Get the form to create a new application + * + * name : developers_application_new + * + * description : Display form to create a new user application + * + * method : GET + * + * parameters : none + * + * return : HTML Response + */ + $controllers->get('/application/new/', $this->call('displayFormApp')) + ->bind('developers_application_new'); + + /** + * Create a new app + * + * name : developers_application + * + * description : POST request to create a new user app + * + * method : POST + * + * parameters : none + * + * return : HTML Response + */ + $controllers->post('/application/', $this->call('newApp')) + ->bind('developers_application'); + + + /** + * Get application information + * + * name : developers_application + * + * description : Get application information + * + * method : GET + * + * parameters : none + * + * return : HTML Response + */ + $controllers->get('/application/{id}/', $this->call('getApp')) + ->assert('id', '\d+') + ->bind('developers_application'); + + /** + * Delete application + * + * name : delete_developers_application + * + * description : Delete selected application + * + * method : GET + * + * parameters : none + * + * return : HTML Response + */ + $controllers->delete('/application/{id}/', $this->call('deleteApp')) + ->assert('id', '\d+') + ->bind('delete_developers_application'); + + /** + * Allow authentification paswword grant method + * + * name : developers_application_authorize_grant_password + * + * description : Authorize application to use a grant password type, which allow end user to + * authenticate himself with their credentials (login/password) + * + * method : POST + * + * parameters : none + * + * return : HTML Response + */ + $controllers->post('/application/{id}/authorize_grant_password/', $this->call('authorizeGrantpassword')) + ->assert('id', '\d+') + ->bind('developers_application_authorize_grant_password'); + + /** + * Renew access token + * + * name : developers_application_token + * + * description : Regenerate an access token for the current app linked to the authenticated user + * + * method : POST + * + * parameters : none + * + * return : HTML Response + */ + $controllers->post('/application/{id}/access_token/', $this->call('renewAccessToken')) + ->assert('id', '\d+') + ->bind('developers_application_token'); + + /** + * Update application callback + * + * name : application_callback + * + * description : Change callback used by application + * + * method : POST + * + * parameters : none + * + * return : HTML Response + */ + $controllers->post('/application/{id}/callback/', $this->call('renewAppCallback')) + ->assert('id', '\d+') + ->bind('application_callback'); + + return $controllers; + } + + /** + * 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 + */ + public function deleteApp(Application $app, Request $request, $id) + { + if ( ! $request->isXmlHttpRequest() || ! array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) { + $app->abort(400, _('Bad request format, only JSON is allowed')); + } + + $error = false; + + try { + $clientApp = new \API_OAuth2_Application(\appbox::get_instance($app['Core']), $id); + $clientApp->delete(); + } catch (\Exception_NotFound $e) { + $error = true; + } + + return new JsonResponse(array('success' => ! $error)); + } + + /** + * 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 + */ + public function renewAppCallback(Application $app, Request $request, $id) + { + if ( ! $request->isXmlHttpRequest() || ! array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) { + $app->abort(400, _('Bad request format, only JSON is allowed')); + } + + $error = false; + + try { + $clientApp = new \API_OAuth2_Application(\appbox::get_instance($app['Core']), $id); + + if ($callback = $request->get("callback")) { + $clientApp->set_redirect_uri($callback); + } else { + $error = true; + } + } catch (\Exception_NotFound $e) { + $error = true; + } + + return new JsonResponse(array('success' => ! $error)); + } + + /** + * 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 + */ + public function renewAccessToken(Application $app, Request $request, $id) + { + if ( ! $request->isXmlHttpRequest() || ! array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) { + $app->abort(400, _('Bad request format, only JSON is allowed')); + } + + $appbox = \appbox::get_instance($app['Core']); + $error = false; + $accessToken = null; + + try { + $clientApp = new \API_OAuth2_Application($appbox, $id); + $account = $clientApp->get_user_account($app['Core']->getAuthenticatedUser()); + + $token = $account->get_token(); + + if ($token instanceof \API_OAuth2_Token) { + $token->renew(); + } else { + $token = \API_OAuth2_Token::create($appbox, $account); + } + + $accessToken = $token->get_value(); + } catch (\Exception $e) { + $error = true; + } + + return new JsonResponse(array('success' => ! $error, 'token' => $accessToken)); + } + + /** + * 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 + */ + public function authorizeGrantpassword(Application $app, Request $request, $id) + { + if ( ! $request->isXmlHttpRequest() || ! array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) { + $app->abort(400, _('Bad request format, only JSON is allowed')); + } + + $error = false; + + try { + $clientApp = new \API_OAuth2_Application(\appbox::get_instance($app['Core']), $id); + } catch (\Exception_NotFound $e) { + $error = true; + } + + $clientApp->set_grant_password((bool) $request->get('grant', false)); + + return new JsonResponse(array('success' => ! $error)); + } + + /** + * 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 + */ + public function newApp(Application $app, Request $request) + { + $error = false; + + if ($request->get("type") == "desktop") { + $form = new \API_OAuth2_Form_DevAppDesktop($app['request']); + } else { + $form = new \API_OAuth2_Form_DevAppInternet($app['request']); + } + + $violations = $app['validator']->validate($form); + + if ($violations->count() == 0) { + $error = true; + } + + if ($error) { + $application = \API_OAuth2_Application::create(\appbox::get_instance($app['Core']), $app['Core']->getAuthenticatedUser(), $form->getName()); + $application + ->set_description($form->getDescription()) + ->set_redirect_uri($form->getSchemeCallback() . $form->getCallback()) + ->set_type($form->getType()) + ->set_website($form->getSchemeWebsite() . $form->getWebsite()); + + return $app->redirect(sprintf('/developers/application/%d/', $application->get_id())); + } + + $var = array( + "violations" => $violations, + "form" => $form + ); + + return $app['Core']['Twig']->render('/developers/application.html.twig', $var); + } + + /** + * 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 + */ + public function listApps(Application $app, Request $request) + { + return $app['Core']['Twig']->render('developers/applications.html.twig', array( + "apps" => \API_OAuth2_Application::load_dev_app_by_user( + \appbox::get_instance($app['Core']), $app['Core']->getAuthenticatedUser() + ))); + } + + /** + * Display form application + * + * @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) + { + return $app['Core']['Twig']->render('developers/application_form.html.twig', array( + "violations" => null, + 'form' => null, + 'request' => $request + )); + } + + /** + * 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 + */ + public function getApp(Application $app, Request $request, $id) + { + $user = $app['Core']->getAuthenticatedUser(); + + try { + $client = new \API_OAuth2_Application(\appbox::get_instance($app['Core']), $id); + } catch (\Exception_NotFound $e) { + $app->abort(404); + } + + $token = $client->get_user_account($user)->get_token()->get_value(); + + return $app['Core']['Twig']->render('developers/application.html.twig', array( + "app" => $client, + "user" => $user, + "token" => $token + )); + } + + /** + * Prefix the method to call with the controller class name + * + * @param string $method The method to call + * @return string + */ + private function call($method) + { + return sprintf('%s::%s', __CLASS__, $method); + } +} diff --git a/lib/Alchemy/Phrasea/Core.php b/lib/Alchemy/Phrasea/Core.php index c77efaee6e..c154ce418b 100644 --- a/lib/Alchemy/Phrasea/Core.php +++ b/lib/Alchemy/Phrasea/Core.php @@ -11,7 +11,6 @@ namespace Alchemy\Phrasea; -use Alchemy\Phrasea\Core\Configuration; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Serializer; @@ -102,6 +101,10 @@ public function __construct($environement = null) return new \Alchemy\Phrasea\Cache\Manager($core, $file); }); + $this['Firewall'] = $this->share(function() { + return new Security\Firewall(); + }); + /** * Set Entity Manager using configuration */ diff --git a/lib/Alchemy/Phrasea/Security/Firewall.php b/lib/Alchemy/Phrasea/Security/Firewall.php new file mode 100644 index 0000000000..e73185172e --- /dev/null +++ b/lib/Alchemy/Phrasea/Security/Firewall.php @@ -0,0 +1,37 @@ +redirect("/setup/"); + } + } + + public function requireAuthentication(Application $app) + { + if ($app['Core']->isAuthenticated()) { + try { + $session = \appbox::get_instance($app['Core'])->get_session(); + $session->open_phrasea_session(); + } catch (\Exception $e) { + + return $app->redirect('/login/logout.php'); + } + } else { + + return $app->redirect('/login/'); + } + + if ($app['Core']->getAuthenticatedUser()->is_guest()) { + + return $app->redirect('/login/'); + } + } +} diff --git a/lib/classes/API/OAuth2/Account.class.php b/lib/classes/API/OAuth2/Account.class.php index 09a65dec80..01ccb12b53 100644 --- a/lib/classes/API/OAuth2/Account.class.php +++ b/lib/classes/API/OAuth2/Account.class.php @@ -281,8 +281,9 @@ public static function load_with_user(appbox &$appbox, API_OAuth2_Application $a $row = $stmt->fetch(PDO::FETCH_ASSOC); $stmt->closeCursor(); - if ( ! $row) + if ( ! $row) { throw new Exception_NotFound(); + } return new self($appbox, $row['api_account_id']); } diff --git a/lib/classes/API/OAuth2/Application.class.php b/lib/classes/API/OAuth2/Application.class.php index ea09d36da1..b756786293 100644 --- a/lib/classes/API/OAuth2/Application.class.php +++ b/lib/classes/API/OAuth2/Application.class.php @@ -145,6 +145,11 @@ public function __construct(appbox &$appbox, $application_id) $stmt = $this->appbox->get_connection()->prepare($sql); $stmt->execute(array(':application_id' => $this->id)); + + if (0 === $stmt->rowCount()) { + throw new \Exception_NotFound(sprintf('Application with id %d not found', $this->id)); + } + $row = $stmt->fetch(PDO::FETCH_ASSOC); $stmt->closeCursor(); $this->creator = ! $row['creator'] ? null : User_Adapter::getInstance($row['creator'], $this->appbox); diff --git a/templates/mobile/login/index.twig b/templates/mobile/login/index.twig index 61974a8756..70a5877218 100644 --- a/templates/mobile/login/index.twig +++ b/templates/mobile/login/index.twig @@ -48,7 +48,7 @@