diff --git a/lib/Alchemy/Phrasea/Application/OAuth2.php b/lib/Alchemy/Phrasea/Application/OAuth2.php index a45218e5a2..3fc2851b5f 100644 --- a/lib/Alchemy/Phrasea/Application/OAuth2.php +++ b/lib/Alchemy/Phrasea/Application/OAuth2.php @@ -14,6 +14,8 @@ namespace Alchemy\Phrasea\Application; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpFoundation\Request; /** * @@ -27,415 +29,451 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @link www.phraseanet.com */ return call_user_func(function() - { - $app = new \Silex\Application(); + { + $app = new \Silex\Application(); + + $app['Core'] = \bootstrap::getCore(); - $app['Core'] = \bootstrap::getCore(); + $app->register(new \Silex\Provider\ValidatorServiceProvider(), array( + 'validator.class_path' => __DIR__ . '/../../../../vendor/symfony/validator', + )); - $app->register(new \Silex\Provider\ValidatorServiceProvider(), array( - 'validator.class_path' => __DIR__ . '/../../../../vendor/symfony/validator', - )); + $app['appbox'] = function() use ($app) + { + return \appbox::get_instance($app['Core']); + }; - $app['appbox'] = function() use ($app) - { - return \appbox::get_instance($app['Core']); - }; + $app['oauth'] = function($app) + { + return new \API_OAuth2_Adapter($app['appbox']); + }; - $app['oauth'] = function($app) - { - return new \API_OAuth2_Adapter($app['appbox']); - }; + /** + * Protected Closure + * @var Closure + * @return Symfony\Component\HttpFoundation\Response + */ + $app['response'] = $app->protect(function ($template, $variable) use ($app) + { + /* @var $twig \Twig_Environment */ + $twig = $app['Core']->getTwig(); - /** - * Protected Closure - * @var Closure - * @return Symfony\Component\HttpFoundation\Response - */ - $app['response'] = $app->protect(function ($template, $variable) use ($app) - { - /* @var $twig \Twig_Environment */ - $twig = $app['Core']->getTwig(); + $response = new Response( + $twig->render($template, $variable) + , 200 + , array('Content-Type' => 'text/html') + ); + $response->setCharset('UTF-8'); - $response = new Response( - $twig->render($template, $variable) - , 200 - , array('Content-Type' => 'text/html') - ); - $response->setCharset('UTF-8'); - - return $response; - }); + return $response; + }); /* * ******************************************************************* - * AUTHENTIFICATION API - */ + * AUTHENTIFICATION API + */ - /** - * AUTHORIZE ENDPOINT - * - * Authorization endpoint - used to obtain authorization from the - * resource owner via user-agent redirection. - */ - $authorize_func = function() use ($app) - { + /** + * AUTHORIZE ENDPOINT + * + * Authorization endpoint - used to obtain authorization from the + * resource owner via user-agent redirection. + */ + $authorize_func = function() use ($app) + { $request = $app['request']; - $oauth2_adapter = $app['oauth']; - /* @var $twig \Twig_Environment */ + $oauth2_adapter = $app['oauth']; + /* @var $twig \Twig_Environment */ $twig = $app['Core']->getTwig(); $session = $app['appbox']->get_session(); - //Check for auth params, send error or redirect if not valid - $params = $oauth2_adapter->getAuthorizationRequestParameters($request); - - $authenticated = $session->is_authenticated(); - $app_authorized = false; - $errorMessage = false; - - $client = \API_OAuth2_Application::load_from_client_id($app['appbox'], $params['client_id']); - - $oauth2_adapter->setClient($client); - - $action_accept = $request->get("action_accept", null); - $action_login = $request->get("action_login", null); - - - $template = "api/auth/end_user_authorization.twig"; - $custom_template = $app['appbox']->get_registry()->get('GV_RootPath') . 'config/templates/web/api/auth/end_user_authorization/' . $client->get_id() . '.twig'; - if (file_exists($custom_template)) - { - $template = 'api/auth/end_user_authorization/' . $client->get_id() . '.twig'; - } - - if ( ! $authenticated) - { - if ($action_login !== null) - { - try - { - $login = $request->get("login"); - $password = $request->get("password"); - $auth = new \Session_Authentication_Native($app['appbox'], $login, $password); - $session->authenticate($auth); - } - catch (\Exception $e) - { - $params = array( - "auth" => $oauth2_adapter - , "session" => $session - , "errorMessage" => true - , "user" => $app['Core']->getAuthenticatedUser() - ); - $html = $twig->render($template, $params); - - return new Response($html, 200, array("content-type" => "text/html")); - } - } - else - { - $params = array( - "auth" => $oauth2_adapter - , "session" => $session - , "errorMessage" => $errorMessage - , "user" => $app['Core']->getAuthenticatedUser() - ); - $html = $twig->render($template, $params); - - return new Response($html, 200, array("content-type" => "text/html")); - } - } - - //check if current client is alreadu authorized by current user - $user_auth_clients = \API_OAuth2_Application::load_authorized_app_by_user($app['appbox'], $app['Core']->getAuthenticatedUser()); - - foreach ($user_auth_clients as $auth_client) - { - if ($client->get_client_id() == $auth_client->get_client_id()) - $app_authorized = true; - } - - $account = $oauth2_adapter->updateAccount($session->get_usr_id()); - $params['account_id'] = $account->get_id(); - - if ( ! $app_authorized && $action_accept === null) - { - $params = array( - "auth" => $oauth2_adapter - , "session" => $session - , "errorMessage" => $errorMessage - , "user" => $app['Core']->getAuthenticatedUser() - ); - - $html = $twig->render($template, $params); - - return new Response($html, 200, array("content-type" => "text/html")); - } - elseif ( ! $app_authorized && $action_accept !== null) - { - $app_authorized = ! ! $action_accept; - $account->set_revoked( ! $app_authorized); - } - - //if native app show template - if ($oauth2_adapter->isNativeApp($params['redirect_uri'])) - { - $params = $oauth2_adapter->finishNativeClientAuthorization($app_authorized, $params); - $params['user'] = $app['Core']->getAuthenticatedUser(); - $html = $twig->render("api/auth/native_app_access_token.twig", $params); - - return new Response($html, 200, array("content-type" => "text/html")); - } - else - { - $oauth2_adapter->finishClientAuthorization($app_authorized, $params); - } - }; - - $route = '/authorize'; - $app->get($route, $authorize_func); - $app->post($route, $authorize_func); - - - - /** - * TOKEN ENDPOINT - * Token endpoint - used to exchange an authorization grant for an access token. - */ - $route = '/token'; - $app->post($route, function() use ($app) - { - $app['oauth']->grantAccessToken(); - ob_flush(); - flush(); - - 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") - ); - }); - - $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+'); - /** - * ******************************************************************* - * - * Route Errors - * - */ - $app->error(function (\Exception $e) use ($app) - { - if ($e instanceof NotFoundHttpException || $e instanceof \Exception_NotFound) - { - return new Response('The requested page could not be found.', 404); - } - - $code = $e instanceof HttpExceptionInterface ? $e->getStatusCode() : 500; - - return new Response('We are sorry, but something went wrong.
' . $e->getMessage(), $code); - }); - - - return $app; - }); + //Check for auth params, send error or redirect if not valid + $params = $oauth2_adapter->getAuthorizationRequestParameters($request); + + $authenticated = $session->is_authenticated(); + $app_authorized = false; + $errorMessage = false; + + $client = \API_OAuth2_Application::load_from_client_id($app['appbox'], $params['client_id']); + + $oauth2_adapter->setClient($client); + + $action_accept = $request->get("action_accept", null); + $action_login = $request->get("action_login", null); + + + $template = "api/auth/end_user_authorization.twig"; + + $custom_template = sprintf( + "%sconfig/templates/web/api/auth/end_user_authorization/%s.twig" + , $app['appbox']->get_registry()->get('GV_RootPath') + , $client->get_id() + ); + + + if (file_exists($custom_template)) + { + $template = sprintf( + 'api/auth/end_user_authorization/%s.twig' + , $client->get_id() + ); + } + + if ( ! $authenticated) + { + if ($action_login !== null) + { + try + { + $login = $request->get("login"); + $password = $request->get("password"); + $auth = new \Session_Authentication_Native($app['appbox'], $login, $password); + $session->authenticate($auth); + } + catch (\Exception $e) + { + $params = array( + "auth" => $oauth2_adapter + , "session" => $session + , "errorMessage" => true + , "user" => $app['Core']->getAuthenticatedUser() + ); + $html = $twig->render($template, $params); + + return new Response($html, 200, array("content-type" => "text/html")); + } + } + else + { + $params = array( + "auth" => $oauth2_adapter + , "session" => $session + , "errorMessage" => $errorMessage + , "user" => $app['Core']->getAuthenticatedUser() + ); + $html = $twig->render($template, $params); + + return new Response($html, 200, array("content-type" => "text/html")); + } + } + + //check if current client is already authorized by current user + $user_auth_clients = \API_OAuth2_Application::load_authorized_app_by_user( + $app['appbox'] + , $app['Core']->getAuthenticatedUser() + ); + + foreach ($user_auth_clients as $auth_client) + { + if ($client->get_client_id() == $auth_client->get_client_id()) + $app_authorized = true; + } + + $account = $oauth2_adapter->updateAccount($session->get_usr_id()); + + $params['account_id'] = $account->get_id(); + + if ( ! $app_authorized && $action_accept === null) + { + $params = array( + "auth" => $oauth2_adapter + , "session" => $session + , "errorMessage" => $errorMessage + , "user" => $app['Core']->getAuthenticatedUser() + ); + + $html = $twig->render($template, $params); + + return new Response($html, 200, array("content-type" => "text/html")); + } + elseif ( ! $app_authorized && $action_accept !== null) + { + $app_authorized = ! ! $action_accept; + $account->set_revoked( ! $app_authorized); + } + + //if native app show template + if ($oauth2_adapter->isNativeApp($params['redirect_uri'])) + { + $params = $oauth2_adapter->finishNativeClientAuthorization($app_authorized, $params); + $params['user'] = $app['Core']->getAuthenticatedUser(); + $html = $twig->render("api/auth/native_app_access_token.twig", $params); + + return new Response($html, 200, array("content-type" => "text/html")); + } + else + { + $oauth2_adapter->finishClientAuthorization($app_authorized, $params); + } + }; + + $route = '/authorize'; + $app->get($route, $authorize_func); + $app->post($route, $authorize_func); + + + + /** + * TOKEN ENDPOINT + * Token endpoint - used to exchange an authorization grant for an access token. + */ + $route = '/token'; + $app->post($route, function(\Silex\Application $app, Request $request) + { + if(!$request->isSecure()) + { + throw new HttpException(400, 'require the use of the https', null, array('content-type'=> 'application/json')); + } + + $app['oauth']->grantAccessToken(); + ob_flush(); + flush(); + + 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") + ); + }); + + $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+'); + /** + * ******************************************************************* + * + * Route Errors + * + */ + $app->error(function (\Exception $e) use ($app) + { + if ($e instanceof NotFoundHttpException || $e instanceof \Exception_NotFound) + { + return new Response('The requested page could not be found.', 404); + } + + $code = 500; + $msg = 'We are sorry, but something went wrong'; + $headers = array(); + + if($e instanceof HttpExceptionInterface) + { + $headers = $e->getHeaders(); + $msg = $e->getMessage(); + $code = $e->getStatusCode(); + + if(isset($headers['content-type']) && $headers['content-type'] == 'application/json') + { + $obj = new \stdClass(); + $obj->msg = $msg; + $obj->code = $code; + $msg = json_encode($obj); + } + } + + return new Response($msg, $code, $headers); + }); + + + return $app; + }); diff --git a/lib/classes/API/OAuth2/Adapter.class.php b/lib/classes/API/OAuth2/Adapter.class.php index d1af88278e..90874bc657 100644 --- a/lib/classes/API/OAuth2/Adapter.class.php +++ b/lib/classes/API/OAuth2/Adapter.class.php @@ -195,7 +195,6 @@ class API_OAuth2_Adapter extends OAuth2 $application = API_OAuth2_Application::load_from_client_id($this->appbox, $client_id); if ($client_secret === NULL) - return true; $crypted = $this->crypt_secret($client_secret, $application->get_nonce()); @@ -204,7 +203,7 @@ class API_OAuth2_Adapter extends OAuth2 } catch (Exception $e) { - + } return false; @@ -249,7 +248,7 @@ class API_OAuth2_Adapter extends OAuth2 } catch (Exception $e) { - + } return $result; @@ -285,7 +284,8 @@ class API_OAuth2_Adapter extends OAuth2 protected function getSupportedGrantTypes() { return array( - OAUTH2_GRANT_TYPE_AUTH_CODE + OAUTH2_GRANT_TYPE_AUTH_CODE, + OAUTH2_GRANT_TYPE_USER_CREDENTIALS ); } @@ -321,7 +321,7 @@ class API_OAuth2_Adapter extends OAuth2 } catch (Exception $e) { - + } return null; @@ -375,7 +375,7 @@ class API_OAuth2_Adapter extends OAuth2 } catch (Exception $e) { - + } return null; @@ -410,12 +410,12 @@ class API_OAuth2_Adapter extends OAuth2 $scope = $request->get('scope', false); $state = $request->get('state', false); - if($state) + if ($state) { $datas["state"] = $state; } - if($scope) + if ($scope) { $datas["scope"] = $scope; } @@ -443,7 +443,7 @@ class API_OAuth2_Adapter extends OAuth2 * check for valid client_id * check for valid redirect_uri */ - if (!$input["client_id"]) + if ( ! $input["client_id"]) { if ($input["redirect_uri"]) $this->errorDoRedirectUriCallback( @@ -462,7 +462,7 @@ class API_OAuth2_Adapter extends OAuth2 /** * At least one of: existing redirect URI or input redirect URI must be specified */ - if (!$redirect_uri && !$input["redirect_uri"]) + if ( ! $redirect_uri && ! $input["redirect_uri"]) $this->errorJsonResponse( OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_REQUEST); @@ -498,16 +498,16 @@ class API_OAuth2_Adapter extends OAuth2 $input["redirect_uri"] = $redirect_uri; } - + /** * Check response_type */ - if (!$input["response_type"]) + if ( ! $input["response_type"]) { $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_REQUEST, 'Invalid response type.', NULL, $input["state"]); } - + /** * Check requested auth response type against the list of supported types */ @@ -521,11 +521,11 @@ class API_OAuth2_Adapter extends OAuth2 if ($this->checkRestrictedAuthResponseType($input["client_id"], $input["response_type"]) === FALSE) $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_UNAUTHORIZED_CLIENT, NULL, NULL, $input["state"]); - + /** * Validate that the requested scope is supported */ - if ($input["scope"] && !$this->checkScope($input["scope"], $this->getSupportedScopes())) + if ($input["scope"] && ! $this->checkScope($input["scope"], $this->getSupportedScopes())) $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_SCOPE, NULL, NULL, $input["state"]); /** @@ -628,7 +628,7 @@ class API_OAuth2_Adapter extends OAuth2 } catch (Exception $e) { - + } return false; @@ -640,13 +640,11 @@ class API_OAuth2_Adapter extends OAuth2 if ($token_param === FALSE) // Access token was not provided - return $exit_not_present ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_BAD_REQUEST, $realm, OAUTH2_ERROR_INVALID_REQUEST, 'The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed.', NULL, $scope) : FALSE; // Get the stored token data (from the implementing subclass) $token = $this->getAccessToken($token_param); if ($token === NULL) - return $exit_invalid ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_UNAUTHORIZED, $realm, OAUTH2_ERROR_INVALID_TOKEN, 'The access token provided is invalid.', NULL, $scope) : FALSE; if (isset($token['revoked']) && $token['revoked']) @@ -658,13 +656,11 @@ class API_OAuth2_Adapter extends OAuth2 { // Check token expiration (I'm leaving this check separated, later we'll fill in better error messages) if (isset($token["expires"]) && time() > $token["expires"]) - return $exit_expired ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_UNAUTHORIZED, $realm, OAUTH2_ERROR_EXPIRED_TOKEN, 'The access token provided has expired.', NULL, $scope) : FALSE; } // Check scope, if provided // If token doesn't have a scope, it's NULL/empty, or it's insufficient, then throw an error - if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->checkScope($scope, $token["scope"]))) - + if ($scope && ( ! isset($token["scope"]) || ! $token["scope"] || ! $this->checkScope($scope, $token["scope"]))) return $exit_scope ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_FORBIDDEN, $realm, OAUTH2_ERROR_INSUFFICIENT_SCOPE, 'The request requires higher privileges than provided by the access token.', NULL, $scope) : FALSE; //save token's linked ses_id @@ -718,11 +714,11 @@ class API_OAuth2_Adapter extends OAuth2 $input = filter_input_array(INPUT_POST, $filters); // Grant Type must be specified. - if (!$input["grant_type"]) + if ( ! $input["grant_type"]) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Invalid grant_type parameter or parameter missing'); // Make sure we've implemented the requested grant type - if (!in_array($input["grant_type"], $this->getSupportedGrantTypes())) + if ( ! in_array($input["grant_type"], $this->getSupportedGrantTypes())) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE); // Authorize the client @@ -731,39 +727,36 @@ class API_OAuth2_Adapter extends OAuth2 if ($this->checkClientCredentials($client[0], $client[1]) === FALSE) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_CLIENT); - if (!$this->checkRestrictedGrantType($client[0], $input["grant_type"])) + if ( ! $this->checkRestrictedGrantType($client[0], $input["grant_type"])) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_UNAUTHORIZED_CLIENT); - + // Do the granting switch ($input["grant_type"]) { case OAUTH2_GRANT_TYPE_AUTH_CODE: - if (!$input["code"] || !$input["redirect_uri"]) + if ( ! $input["code"] || ! $input["redirect_uri"]) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST); - $stored = $this->getAuthCode($input["code"]); // Ensure that the input uri starts with the stored uri if ($stored === NULL || (strcasecmp(substr($input["redirect_uri"], 0, strlen($stored["redirect_uri"])), $stored["redirect_uri"]) !== 0) || $client[0] != $stored["client_id"]) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT); - if ($stored["expires"] < time()) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_EXPIRED_TOKEN); - break; case OAUTH2_GRANT_TYPE_USER_CREDENTIALS: - if (!$input["username"] || !$input["password"]) + if ( ! $input["username"] || ! $input["password"]) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Missing parameters. "username" and "password" required'); $stored = $this->checkUserCredentials($client[0], $input["username"], $input["password"]); if ($stored === FALSE) - $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT); + $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT, 'Unknow user'); break; case OAUTH2_GRANT_TYPE_ASSERTION: - if (!$input["assertion_type"] || !$input["assertion"]) + if ( ! $input["assertion_type"] || ! $input["assertion"]) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST); $stored = $this->checkAssertion($client[0], $input["assertion_type"], $input["assertion"]); @@ -773,7 +766,7 @@ class API_OAuth2_Adapter extends OAuth2 break; case OAUTH2_GRANT_TYPE_REFRESH_TOKEN: - if (!$input["refresh_token"]) + if ( ! $input["refresh_token"]) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'No "refresh_token" parameter found'); $stored = $this->getRefreshToken($input["refresh_token"]); @@ -796,10 +789,10 @@ class API_OAuth2_Adapter extends OAuth2 } // Check scope, if provided - if ($input["scope"] && (!is_array($stored) || !isset($stored["scope"]) || !$this->checkScope($input["scope"], $stored["scope"]))) + if ($input["scope"] && ( ! is_array($stored) || ! isset($stored["scope"]) || ! $this->checkScope($input["scope"], $stored["scope"]))) $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_SCOPE); - if (!$input["scope"]) + if ( ! $input["scope"]) $input["scope"] = NULL; $token = $this->createAccessToken($stored['account_id'], $input["scope"]); @@ -835,4 +828,30 @@ class API_OAuth2_Adapter extends OAuth2 return $token; } + protected function checkUserCredentials($client_id, $username, $password) + { + try + { + $appbox = appbox::get_instance(\bootstrap::getCore()); + + $application = API_OAuth2_Application::load_from_client_id($appbox, $client_id); + + $auth = new \Session_Authentication_Native($appbox, $username, $password); + + $auth->challenge_password(); + + $account = API_OAuth2_Account::load_with_user($appbox, $application, $auth->get_user()); + + return array( + 'redirect_uri' => $application->get_redirect_uri() + , 'client_id' => $application->get_client_id() + , 'account_id' => $account->get_id() + ); + } + catch (\Exception $e) + { + return false; + } + } + } diff --git a/www/api/oauthv2/index.php b/www/api/oauthv2/index.php index 0b93c0527a..87ee89069e 100644 --- a/www/api/oauthv2/index.php +++ b/www/api/oauthv2/index.php @@ -15,14 +15,8 @@ * @license http://opensource.org/licenses/gpl-3.0 GPLv3 * @link www.phraseanet.com */ -use Symfony\Component\HttpFoundation\Response; +require_once __DIR__ . "/../../../lib/bootstrap.php"; -try -{ - $app = require __DIR__ . '/../../../lib/Alchemy/Phrasea/Application/OAuth2.php'; - $app->run(); -} -catch (Exception $e) -{ - return new Response('Internal Server Error', 500); -} +$app = require __DIR__ . '/../../../lib/Alchemy/Phrasea/Application/OAuth2.php'; + +$app->run();