implements oauth2.0 v9.0 Resource Owner Basic Credentials

This commit is contained in:
Nicolas Le Goff
2012-04-13 15:44:39 +02:00
parent e513a75d86
commit 527b3639b5
3 changed files with 493 additions and 442 deletions

View File

@@ -14,6 +14,8 @@ namespace Alchemy\Phrasea\Application;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; 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 * @link www.phraseanet.com
*/ */
return call_user_func(function() 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( $app->register(new \Silex\Provider\ValidatorServiceProvider(), array(
'validator.class_path' => __DIR__ . '/../../../../vendor/symfony/validator', 'validator.class_path' => __DIR__ . '/../../../../vendor/symfony/validator',
)); ));
$app['appbox'] = function() use ($app) $app['appbox'] = function() use ($app)
{ {
return \appbox::get_instance($app['Core']); return \appbox::get_instance($app['Core']);
}; };
$app['oauth'] = function($app) $app['oauth'] = function($app)
{ {
return new \API_OAuth2_Adapter($app['appbox']); return new \API_OAuth2_Adapter($app['appbox']);
}; };
/** /**
* Protected Closure * Protected Closure
* @var Closure * @var Closure
* @return Symfony\Component\HttpFoundation\Response * @return Symfony\Component\HttpFoundation\Response
*/ */
$app['response'] = $app->protect(function ($template, $variable) use ($app) $app['response'] = $app->protect(function ($template, $variable) use ($app)
{ {
/* @var $twig \Twig_Environment */ /* @var $twig \Twig_Environment */
$twig = $app['Core']->getTwig(); $twig = $app['Core']->getTwig();
$response = new Response( $response = new Response(
$twig->render($template, $variable) $twig->render($template, $variable)
, 200 , 200
, array('Content-Type' => 'text/html') , array('Content-Type' => 'text/html')
); );
$response->setCharset('UTF-8'); $response->setCharset('UTF-8');
return $response; return $response;
}); });
/* * ******************************************************************* /* * *******************************************************************
* AUTHENTIFICATION API * AUTHENTIFICATION API
*/ */
/** /**
* AUTHORIZE ENDPOINT * AUTHORIZE ENDPOINT
* *
* Authorization endpoint - used to obtain authorization from the * Authorization endpoint - used to obtain authorization from the
* resource owner via user-agent redirection. * resource owner via user-agent redirection.
*/ */
$authorize_func = function() use ($app) $authorize_func = function() use ($app)
{ {
$request = $app['request']; $request = $app['request'];
$oauth2_adapter = $app['oauth']; $oauth2_adapter = $app['oauth'];
/* @var $twig \Twig_Environment */ /* @var $twig \Twig_Environment */
$twig = $app['Core']->getTwig(); $twig = $app['Core']->getTwig();
$session = $app['appbox']->get_session(); $session = $app['appbox']->get_session();
//Check for auth params, send error or redirect if not valid //Check for auth params, send error or redirect if not valid
$params = $oauth2_adapter->getAuthorizationRequestParameters($request); $params = $oauth2_adapter->getAuthorizationRequestParameters($request);
$authenticated = $session->is_authenticated(); $authenticated = $session->is_authenticated();
$app_authorized = false; $app_authorized = false;
$errorMessage = false; $errorMessage = false;
$client = \API_OAuth2_Application::load_from_client_id($app['appbox'], $params['client_id']); $client = \API_OAuth2_Application::load_from_client_id($app['appbox'], $params['client_id']);
$oauth2_adapter->setClient($client); $oauth2_adapter->setClient($client);
$action_accept = $request->get("action_accept", null); $action_accept = $request->get("action_accept", null);
$action_login = $request->get("action_login", null); $action_login = $request->get("action_login", null);
$template = "api/auth/end_user_authorization.twig"; $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)) $custom_template = sprintf(
{ "%sconfig/templates/web/api/auth/end_user_authorization/%s.twig"
$template = 'api/auth/end_user_authorization/' . $client->get_id() . '.twig'; , $app['appbox']->get_registry()->get('GV_RootPath')
} , $client->get_id()
);
if ( ! $authenticated)
{
if ($action_login !== null) if (file_exists($custom_template))
{ {
try $template = sprintf(
{ 'api/auth/end_user_authorization/%s.twig'
$login = $request->get("login"); , $client->get_id()
$password = $request->get("password"); );
$auth = new \Session_Authentication_Native($app['appbox'], $login, $password); }
$session->authenticate($auth);
} if ( ! $authenticated)
catch (\Exception $e) {
{ if ($action_login !== null)
$params = array( {
"auth" => $oauth2_adapter try
, "session" => $session {
, "errorMessage" => true $login = $request->get("login");
, "user" => $app['Core']->getAuthenticatedUser() $password = $request->get("password");
); $auth = new \Session_Authentication_Native($app['appbox'], $login, $password);
$html = $twig->render($template, $params); $session->authenticate($auth);
}
return new Response($html, 200, array("content-type" => "text/html")); catch (\Exception $e)
} {
} $params = array(
else "auth" => $oauth2_adapter
{ , "session" => $session
$params = array( , "errorMessage" => true
"auth" => $oauth2_adapter , "user" => $app['Core']->getAuthenticatedUser()
, "session" => $session );
, "errorMessage" => $errorMessage $html = $twig->render($template, $params);
, "user" => $app['Core']->getAuthenticatedUser()
); return new Response($html, 200, array("content-type" => "text/html"));
$html = $twig->render($template, $params); }
}
return new Response($html, 200, array("content-type" => "text/html")); else
} {
} $params = array(
"auth" => $oauth2_adapter
//check if current client is alreadu authorized by current user , "session" => $session
$user_auth_clients = \API_OAuth2_Application::load_authorized_app_by_user($app['appbox'], $app['Core']->getAuthenticatedUser()); , "errorMessage" => $errorMessage
, "user" => $app['Core']->getAuthenticatedUser()
foreach ($user_auth_clients as $auth_client) );
{ $html = $twig->render($template, $params);
if ($client->get_client_id() == $auth_client->get_client_id())
$app_authorized = true; return new Response($html, 200, array("content-type" => "text/html"));
} }
}
$account = $oauth2_adapter->updateAccount($session->get_usr_id());
$params['account_id'] = $account->get_id(); //check if current client is already authorized by current user
$user_auth_clients = \API_OAuth2_Application::load_authorized_app_by_user(
if ( ! $app_authorized && $action_accept === null) $app['appbox']
{ , $app['Core']->getAuthenticatedUser()
$params = array( );
"auth" => $oauth2_adapter
, "session" => $session foreach ($user_auth_clients as $auth_client)
, "errorMessage" => $errorMessage {
, "user" => $app['Core']->getAuthenticatedUser() if ($client->get_client_id() == $auth_client->get_client_id())
); $app_authorized = true;
}
$html = $twig->render($template, $params);
$account = $oauth2_adapter->updateAccount($session->get_usr_id());
return new Response($html, 200, array("content-type" => "text/html"));
} $params['account_id'] = $account->get_id();
elseif ( ! $app_authorized && $action_accept !== null)
{ if ( ! $app_authorized && $action_accept === null)
$app_authorized = ! ! $action_accept; {
$account->set_revoked( ! $app_authorized); $params = array(
} "auth" => $oauth2_adapter
, "session" => $session
//if native app show template , "errorMessage" => $errorMessage
if ($oauth2_adapter->isNativeApp($params['redirect_uri'])) , "user" => $app['Core']->getAuthenticatedUser()
{ );
$params = $oauth2_adapter->finishNativeClientAuthorization($app_authorized, $params);
$params['user'] = $app['Core']->getAuthenticatedUser(); $html = $twig->render($template, $params);
$html = $twig->render("api/auth/native_app_access_token.twig", $params);
return new Response($html, 200, array("content-type" => "text/html"));
return new Response($html, 200, array("content-type" => "text/html")); }
} elseif ( ! $app_authorized && $action_accept !== null)
else {
{ $app_authorized = ! ! $action_accept;
$oauth2_adapter->finishClientAuthorization($app_authorized, $params); $account->set_revoked( ! $app_authorized);
} }
};
//if native app show template
$route = '/authorize'; if ($oauth2_adapter->isNativeApp($params['redirect_uri']))
$app->get($route, $authorize_func); {
$app->post($route, $authorize_func); $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"));
* TOKEN ENDPOINT }
* Token endpoint - used to exchange an authorization grant for an access token. else
*/ {
$route = '/token'; $oauth2_adapter->finishClientAuthorization($app_authorized, $params);
$app->post($route, function() use ($app) }
{ };
$app['oauth']->grantAccessToken();
ob_flush(); $route = '/authorize';
flush(); $app->get($route, $authorize_func);
$app->post($route, $authorize_func);
return;
});
/**
/** * TOKEN ENDPOINT
* MANAGEMENT APPS * Token endpoint - used to exchange an authorization grant for an access token.
* */
* $route = '/token';
*/ $app->post($route, function(\Silex\Application $app, Request $request)
/** {
* list of all authorized apps by logged user if(!$request->isSecure())
*/ {
$route = '/applications'; throw new HttpException(400, 'require the use of the https', null, array('content-type'=> 'application/json'));
$app->get($route, function() use ($app) }
{
$apps = \API_OAuth2_Application::load_app_by_user($app['appbox'], $app['Core']->getAuthenticatedUser()); $app['oauth']->grantAccessToken();
ob_flush();
return $app['response']('api/auth/applications.twig', array("apps" => $apps, 'user' => $app['Core']->getAuthenticatedUser())); flush();
});
return;
/** });
* list of apps created by user
*/
$route = "/applications/dev"; /**
$app->get($route, function() use ($app) * MANAGEMENT APPS
{ *
$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)); /**
}); * list of all authorized apps by logged user
*/
/** $route = '/applications';
* display a new app form $app->get($route, function() use ($app)
*/ {
$route = "/applications/dev/new"; $apps = \API_OAuth2_Application::load_app_by_user($app['appbox'], $app['Core']->getAuthenticatedUser());
$app->get($route, function() use ($app)
{ return $app['response']('api/auth/applications.twig', array("apps" => $apps, 'user' => $app['Core']->getAuthenticatedUser()));
$var = array("violations" => null, 'form' => null, 'request' => $app['request']); });
return $app['response']('api/auth/application_dev_new.twig', $var); /**
}); * list of apps created by user
*/
$route = "/applications/dev";
$app->get($route, function() use ($app)
$route = "/applications/dev/create"; {
$app->post($route, function() use ($app) $rs = \API_OAuth2_Application::load_dev_app_by_user($app['appbox'], $app['Core']->getAuthenticatedUser());
{
$submit = false; return $app['response']('api/auth/application_dev.twig', array("apps" => $rs));
if ($app['request']->get("type") == "desktop") });
{
$post = new \API_OAuth2_Form_DevAppDesktop($app['request']); /**
} * display a new app form
else */
{ $route = "/applications/dev/new";
$post = new \API_OAuth2_Form_DevAppInternet($app['request']); $app->get($route, function() use ($app)
} {
$var = array("violations" => null, 'form' => null, 'request' => $app['request']);
$violations = $app['validator']->validate($post);
return $app['response']('api/auth/application_dev_new.twig', $var);
if ($violations->count() == 0) });
$submit = true;
$request = $app['request'];
$route = "/applications/dev/create";
if ($submit) $app->post($route, function() use ($app)
{ {
$application = \API_OAuth2_Application::create($app['appbox'], $app['Core']->getAuthenticatedUser(), $post->getName()); $submit = false;
$application->set_description($post->getDescription()) if ($app['request']->get("type") == "desktop")
->set_redirect_uri($post->getSchemeCallback() . $post->getCallback()) {
->set_type($post->getType()) $post = new \API_OAuth2_Form_DevAppDesktop($app['request']);
->set_website($post->getSchemeWebsite() . $post->getWebsite()); }
else
return $app->redirect("/api/oauthv2/applications/dev/" . $application->get_id() . "/show"); {
} $post = new \API_OAuth2_Form_DevAppInternet($app['request']);
}
$var = array(
"violations" => $violations, $violations = $app['validator']->validate($post);
"form" => $post
); if ($violations->count() == 0)
$submit = true;
return $app['response']('api/auth/application_dev_new.twig', $var);
}); $request = $app['request'];
if ($submit)
/** {
* show details of app identified by its id $application = \API_OAuth2_Application::create($app['appbox'], $app['Core']->getAuthenticatedUser(), $post->getName());
*/ $application->set_description($post->getDescription())
$route = "/applications/dev/{id}/show"; ->set_redirect_uri($post->getSchemeCallback() . $post->getCallback())
$app->get($route, function($id) use ($app) ->set_type($post->getType())
{ ->set_website($post->getSchemeWebsite() . $post->getWebsite());
$client = new \API_OAuth2_Application($app['appbox'], $id);
$token = $client->get_user_account($app['Core']->getAuthenticatedUser())->get_token()->get_value(); return $app->redirect("/api/oauthv2/applications/dev/" . $application->get_id() . "/show");
$var = array("app" => $client, "user" => $app['Core']->getAuthenticatedUser(), "token" => $token); }
return $app['response']('api/auth/application_dev_show.twig', $var); $var = array(
})->assert('id', '\d+'); "violations" => $violations,
"form" => $post
/** );
* revoke access from a user to the app
* identified by account id return $app['response']('api/auth/application_dev_new.twig', $var);
*/ });
$route = "/applications/revoke_access/";
$app->post($route, function() use ($app)
{ /**
$result = array("ok" => false); * show details of app identified by its id
try */
{ $route = "/applications/dev/{id}/show";
$account = new \API_OAuth2_Account($app['appbox'], $app['request']->get('account_id')); $app->get($route, function($id) use ($app)
$account->set_revoked((bool) $app['request']->get('revoke')); {
$result['ok'] = true; $client = new \API_OAuth2_Application($app['appbox'], $id);
} $token = $client->get_user_account($app['Core']->getAuthenticatedUser())->get_token()->get_value();
catch (\Exception $e) $var = array("app" => $client, "user" => $app['Core']->getAuthenticatedUser(), "token" => $token);
{
return $app['response']('api/auth/application_dev_show.twig', $var);
} })->assert('id', '\d+');
$Serializer = $app['Core']['Serializer']; /**
* revoke access from a user to the app
return new Response( * identified by account id
$Serializer->serialize($result, 'json') */
, 200 $route = "/applications/revoke_access/";
, array("content-type" => "application/json") $app->post($route, function() use ($app)
); {
}); $result = array("ok" => false);
try
$route = "/applications/{id}/generate_access_token/"; {
$app->post($route, function($id) use ($app) $account = new \API_OAuth2_Account($app['appbox'], $app['request']->get('account_id'));
{ $account->set_revoked((bool) $app['request']->get('revoke'));
$result = array("ok" => false); $result['ok'] = true;
try }
{ catch (\Exception $e)
$client = new \API_OAuth2_Application($app['appbox'], $id); {
$account = $client->get_user_account($app['Core']->getAuthenticatedUser());
}
$token = $account->get_token();
$Serializer = $app['Core']['Serializer'];
if ($token instanceof API_OAuth2_Token)
$token->renew(); return new Response(
else $Serializer->serialize($result, 'json')
$token = \API_OAuth2_Token::create($app['appbox'], $account); , 200
, array("content-type" => "application/json")
$result = array( );
"ok" => true });
, 'token' => $token->get_value()
); $route = "/applications/{id}/generate_access_token/";
} $app->post($route, function($id) use ($app)
catch (\Exception $e) {
{ $result = array("ok" => false);
try
} {
$client = new \API_OAuth2_Application($app['appbox'], $id);
$Serializer = $app['Core']['Serializer']; $account = $client->get_user_account($app['Core']->getAuthenticatedUser());
return new Response( $token = $account->get_token();
$Serializer->serialize($result, 'json')
, 200 if ($token instanceof API_OAuth2_Token)
, array("content-type" => "application/json") $token->renew();
); else
})->assert('id', '\d+'); $token = \API_OAuth2_Token::create($app['appbox'], $account);
$route = "/applications/oauth_callback"; $result = array(
$app->post($route, function() use ($app) "ok" => true
{ , 'token' => $token->get_value()
$app_id = $app['request']->request->get("app_id"); );
$app_callback = $app["request"]->request->get("callback"); }
$result = array("success" => false); catch (\Exception $e)
try {
{
$client = new \API_OAuth2_Application($app['appbox'], $app_id); }
$client->set_redirect_uri($app_callback);
$result['success'] = true; $Serializer = $app['Core']['Serializer'];
}
catch (\Exception $e) return new Response(
{ $Serializer->serialize($result, 'json')
, 200
} , array("content-type" => "application/json")
);
$Serializer = $app['Core']['Serializer']; })->assert('id', '\d+');
return new Response( $route = "/applications/oauth_callback";
$Serializer->serialize($result, 'json') $app->post($route, function() use ($app)
, 200 {
, array("content-type" => "application/json") $app_id = $app['request']->request->get("app_id");
); $app_callback = $app["request"]->request->get("callback");
}); $result = array("success" => false);
try
$route = "/applications/{id}"; {
$app->delete($route, function($id) use ($app) $client = new \API_OAuth2_Application($app['appbox'], $app_id);
{ $client->set_redirect_uri($app_callback);
$result = array("success" => false); $result['success'] = true;
try }
{ catch (\Exception $e)
$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
$Serializer = $app['Core']['Serializer']; , array("content-type" => "application/json")
);
return new Response( });
$Serializer->serialize($result, 'json')
, 200 $route = "/applications/{id}";
, array("content-type" => "application/json") $app->delete($route, function($id) use ($app)
); {
})->assert('id', '\d+'); $result = array("success" => false);
/** try
* ******************************************************************* {
* $client = new \API_OAuth2_Application($app['appbox'], $id);
* Route Errors $client->delete();
* $result['success'] = true;
*/ }
$app->error(function (\Exception $e) use ($app) catch (\Exception $e)
{ {
if ($e instanceof NotFoundHttpException || $e instanceof \Exception_NotFound)
{ }
return new Response('The requested page could not be found.', 404);
} $Serializer = $app['Core']['Serializer'];
$code = $e instanceof HttpExceptionInterface ? $e->getStatusCode() : 500; return new Response(
$Serializer->serialize($result, 'json')
return new Response('We are sorry, but something went wrong.<br />' . $e->getMessage(), $code); , 200
}); , array("content-type" => "application/json")
);
})->assert('id', '\d+');
return $app; /**
}); * *******************************************************************
*
* 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;
});

View File

@@ -195,7 +195,6 @@ class API_OAuth2_Adapter extends OAuth2
$application = API_OAuth2_Application::load_from_client_id($this->appbox, $client_id); $application = API_OAuth2_Application::load_from_client_id($this->appbox, $client_id);
if ($client_secret === NULL) if ($client_secret === NULL)
return true; return true;
$crypted = $this->crypt_secret($client_secret, $application->get_nonce()); $crypted = $this->crypt_secret($client_secret, $application->get_nonce());
@@ -285,7 +284,8 @@ class API_OAuth2_Adapter extends OAuth2
protected function getSupportedGrantTypes() protected function getSupportedGrantTypes()
{ {
return array( return array(
OAUTH2_GRANT_TYPE_AUTH_CODE OAUTH2_GRANT_TYPE_AUTH_CODE,
OAUTH2_GRANT_TYPE_USER_CREDENTIALS
); );
} }
@@ -410,12 +410,12 @@ class API_OAuth2_Adapter extends OAuth2
$scope = $request->get('scope', false); $scope = $request->get('scope', false);
$state = $request->get('state', false); $state = $request->get('state', false);
if($state) if ($state)
{ {
$datas["state"] = $state; $datas["state"] = $state;
} }
if($scope) if ($scope)
{ {
$datas["scope"] = $scope; $datas["scope"] = $scope;
} }
@@ -443,7 +443,7 @@ class API_OAuth2_Adapter extends OAuth2
* check for valid client_id * check for valid client_id
* check for valid redirect_uri * check for valid redirect_uri
*/ */
if (!$input["client_id"]) if ( ! $input["client_id"])
{ {
if ($input["redirect_uri"]) if ($input["redirect_uri"])
$this->errorDoRedirectUriCallback( $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 * 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( $this->errorJsonResponse(
OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_REQUEST); OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_REQUEST);
@@ -502,7 +502,7 @@ class API_OAuth2_Adapter extends OAuth2
/** /**
* Check response_type * 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"]); $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_REQUEST, 'Invalid response type.', NULL, $input["state"]);
} }
@@ -525,7 +525,7 @@ class API_OAuth2_Adapter extends OAuth2
/** /**
* Validate that the requested scope is supported * 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"]); $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_SCOPE, NULL, NULL, $input["state"]);
/** /**
@@ -640,13 +640,11 @@ class API_OAuth2_Adapter extends OAuth2
if ($token_param === FALSE) // Access token was not provided 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; 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) // Get the stored token data (from the implementing subclass)
$token = $this->getAccessToken($token_param); $token = $this->getAccessToken($token_param);
if ($token === NULL) 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; 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']) 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) // Check token expiration (I'm leaving this check separated, later we'll fill in better error messages)
if (isset($token["expires"]) && time() > $token["expires"]) 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; 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 // Check scope, if provided
// If token doesn't have a scope, it's NULL/empty, or it's insufficient, then throw an error // 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; 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 //save token's linked ses_id
@@ -718,11 +714,11 @@ class API_OAuth2_Adapter extends OAuth2
$input = filter_input_array(INPUT_POST, $filters); $input = filter_input_array(INPUT_POST, $filters);
// Grant Type must be specified. // 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'); $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 // 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); $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE);
// Authorize the client // Authorize the client
@@ -731,39 +727,36 @@ class API_OAuth2_Adapter extends OAuth2
if ($this->checkClientCredentials($client[0], $client[1]) === FALSE) if ($this->checkClientCredentials($client[0], $client[1]) === FALSE)
$this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_CLIENT); $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); $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_UNAUTHORIZED_CLIENT);
// Do the granting // Do the granting
switch ($input["grant_type"]) switch ($input["grant_type"])
{ {
case OAUTH2_GRANT_TYPE_AUTH_CODE: 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); $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST);
$stored = $this->getAuthCode($input["code"]); $stored = $this->getAuthCode($input["code"]);
// Ensure that the input uri starts with the stored uri // 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"]) 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); $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT);
if ($stored["expires"] < time()) if ($stored["expires"] < time())
$this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_EXPIRED_TOKEN); $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_EXPIRED_TOKEN);
break; break;
case OAUTH2_GRANT_TYPE_USER_CREDENTIALS: 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'); $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"]); $stored = $this->checkUserCredentials($client[0], $input["username"], $input["password"]);
if ($stored === FALSE) 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; break;
case OAUTH2_GRANT_TYPE_ASSERTION: 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); $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST);
$stored = $this->checkAssertion($client[0], $input["assertion_type"], $input["assertion"]); $stored = $this->checkAssertion($client[0], $input["assertion_type"], $input["assertion"]);
@@ -773,7 +766,7 @@ class API_OAuth2_Adapter extends OAuth2
break; break;
case OAUTH2_GRANT_TYPE_REFRESH_TOKEN: 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'); $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'No "refresh_token" parameter found');
$stored = $this->getRefreshToken($input["refresh_token"]); $stored = $this->getRefreshToken($input["refresh_token"]);
@@ -796,10 +789,10 @@ class API_OAuth2_Adapter extends OAuth2
} }
// Check scope, if provided // 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); $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_SCOPE);
if (!$input["scope"]) if ( ! $input["scope"])
$input["scope"] = NULL; $input["scope"] = NULL;
$token = $this->createAccessToken($stored['account_id'], $input["scope"]); $token = $this->createAccessToken($stored['account_id'], $input["scope"]);
@@ -835,4 +828,30 @@ class API_OAuth2_Adapter extends OAuth2
return $token; 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;
}
}
} }

View File

@@ -15,14 +15,8 @@
* @license http://opensource.org/licenses/gpl-3.0 GPLv3 * @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com * @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 = require __DIR__ . '/../../../lib/Alchemy/Phrasea/Application/OAuth2.php'; $app->run();
$app->run();
}
catch (Exception $e)
{
return new Response('Internal Server Error', 500);
}