Merge pull request #1021 from nlegoff/api-doctrine

[READY][3.9] Transform api_* tables to doctrine entities
This commit is contained in:
Nicolas Le Goff
2014-06-13 14:13:23 +02:00
77 changed files with 4294 additions and 3855 deletions

View File

@@ -71,14 +71,17 @@ use Alchemy\Phrasea\Controller\Utils\ConnectionTest;
use Alchemy\Phrasea\Controller\Utils\PathFileTest; use Alchemy\Phrasea\Controller\Utils\PathFileTest;
use Alchemy\Phrasea\Controller\User\Notifications; use Alchemy\Phrasea\Controller\User\Notifications;
use Alchemy\Phrasea\Controller\User\Preferences; use Alchemy\Phrasea\Controller\User\Preferences;
use Alchemy\Phrasea\Core\Middleware\TokenMiddlewareProvider;
use Alchemy\Phrasea\Core\PhraseaExceptionHandler; use Alchemy\Phrasea\Core\PhraseaExceptionHandler;
use Alchemy\Phrasea\Core\Event\Subscriber\LogoutSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\LogoutSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaLocaleSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaLocaleSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\MaintenanceSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\MaintenanceSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\CookiesDisablerSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\CookiesDisablerSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaInstallSubscriber;
use Alchemy\Phrasea\Core\Middleware\ApiApplicationMiddlewareProvider;
use Alchemy\Phrasea\Core\Middleware\BasketMiddlewareProvider; use Alchemy\Phrasea\Core\Middleware\BasketMiddlewareProvider;
use Alchemy\Phrasea\Core\Middleware\TokenMiddlewareProvider;
use Alchemy\Phrasea\Core\Provider\ACLServiceProvider; use Alchemy\Phrasea\Core\Provider\ACLServiceProvider;
use Alchemy\Phrasea\Core\Provider\APIServiceProvider;
use Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider; use Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider;
use Alchemy\Phrasea\Core\Provider\BrowserServiceProvider; use Alchemy\Phrasea\Core\Provider\BrowserServiceProvider;
use Alchemy\Phrasea\Core\Provider\BorderManagerServiceProvider; use Alchemy\Phrasea\Core\Provider\BorderManagerServiceProvider;
@@ -213,8 +216,10 @@ class Application extends SilexApplication
$this->register(new BasketMiddlewareProvider()); $this->register(new BasketMiddlewareProvider());
$this->register(new TokenMiddlewareProvider()); $this->register(new TokenMiddlewareProvider());
$this->register(new ApiApplicationMiddlewareProvider());
$this->register(new ACLServiceProvider()); $this->register(new ACLServiceProvider());
$this->register(new APIServiceProvider());
$this->register(new AuthenticationManagerServiceProvider()); $this->register(new AuthenticationManagerServiceProvider());
$this->register(new BorderManagerServiceProvider()); $this->register(new BorderManagerServiceProvider());
$this->register(new BrowserServiceProvider()); $this->register(new BrowserServiceProvider());
@@ -472,6 +477,7 @@ class Application extends SilexApplication
$dispatcher->addSubscriber(new PhraseaLocaleSubscriber($app)); $dispatcher->addSubscriber(new PhraseaLocaleSubscriber($app));
$dispatcher->addSubscriber(new MaintenanceSubscriber($app)); $dispatcher->addSubscriber(new MaintenanceSubscriber($app));
$dispatcher->addSubscriber(new CookiesDisablerSubscriber($app)); $dispatcher->addSubscriber(new CookiesDisablerSubscriber($app));
$dispatcher->addSubscriber(new PhraseaInstallSubscriber($app));
return $dispatcher; return $dispatcher;
}) })

View File

@@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\Command\Developer;
use Alchemy\Phrasea\Border\Manager; use Alchemy\Phrasea\Border\Manager;
use Alchemy\Phrasea\Command\Command; use Alchemy\Phrasea\Command\Command;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Model\Entities\AuthFailure; use Alchemy\Phrasea\Model\Entities\AuthFailure;
use Alchemy\Phrasea\Model\Entities\AggregateToken; use Alchemy\Phrasea\Model\Entities\AggregateToken;
use Alchemy\Phrasea\Model\Entities\Basket; use Alchemy\Phrasea\Model\Entities\Basket;
@@ -88,6 +89,8 @@ class RegenerateSqliteDb extends Command
$this->generateUsers($this->container['EM'], $DI); $this->generateUsers($this->container['EM'], $DI);
$this->insertOauthApps($DI); $this->insertOauthApps($DI);
$this->insertOauthAccounts($DI);
$this->insertNativeApps();
$this->generateCollection($DI); $this->generateCollection($DI);
$this->generateRecord($DI); $this->generateRecord($DI);
$this->insertTwoTasks($this->container['EM']); $this->insertTwoTasks($this->container['EM']);
@@ -124,8 +127,10 @@ class RegenerateSqliteDb extends Command
$fixtures['user']['test_phpunit_alt2'] = $DI['user_alt2']->getId(); $fixtures['user']['test_phpunit_alt2'] = $DI['user_alt2']->getId();
$fixtures['user']['user_guest'] = $DI['user_guest']->getId(); $fixtures['user']['user_guest'] = $DI['user_guest']->getId();
$fixtures['oauth']['user'] = $DI['app-user']->get_id(); $fixtures['oauth']['user'] = $DI['api-app-user']->getId();
$fixtures['oauth']['user_notAdmin'] = $DI['app-user_notAdmin']->get_id(); $fixtures['oauth']['acc-user'] = $DI['api-app-acc-user']->getId();
$fixtures['oauth']['user-not-admin'] = $DI['api-app-user-not-admin']->getId();
$fixtures['oauth']['acc-user-not-admin'] = $DI['api-app-acc-user-not-admin']->getId();
$fixtures['databox']['records'] = $DI['databox']->get_sbas_id(); $fixtures['databox']['records'] = $DI['databox']->get_sbas_id();
$fixtures['collection']['coll'] = $DI['coll']->get_base_id(); $fixtures['collection']['coll'] = $DI['coll']->get_base_id();
@@ -182,15 +187,64 @@ class RegenerateSqliteDb extends Command
private function insertOauthApps(\Pimple $DI) private function insertOauthApps(\Pimple $DI)
{ {
$DI['app-user'] = \API_OAuth2_Application::create($this->container, $DI['user'], 'test application for user'); $DI['api-app-user'] = $this->container['manipulator.api-application']->create(
$DI['app-user']->set_redirect_uri('http://callback.com/callback/'); 'test application for user',
$DI['app-user']->set_website('http://website.com/'); ApiApplication::WEB_TYPE,
$DI['app-user']->set_type(\API_OAuth2_Application::WEB_TYPE); 'an api application description',
'http://website.com/',
$DI['user'],
'http://callback.com/callback/'
);
$DI['app-user_notAdmin'] = \API_OAuth2_Application::create($this->container, $DI['user_notAdmin'], 'test application for user not admin'); $DI['api-app-user-not-admin'] = $this->container['manipulator.api-application']->create(
$DI['app-user_notAdmin']->set_redirect_uri('http://callback.com/callback/'); 'test application for user',
$DI['app-user_notAdmin']->set_website('http://website.com/'); ApiApplication::WEB_TYPE,
$DI['app-user_notAdmin']->set_type(\API_OAuth2_Application::WEB_TYPE); 'an api application description',
'http://website.com/',
$DI['user_notAdmin'],
'http://callback.com/callback/'
);
}
public function insertOauthAccounts(\Pimple $DI)
{
$DI['api-app-acc-user'] = $this->container['manipulator.api-account']->create($DI['api-app-user'], $DI['user']);
$this->container['manipulator.api-oauth-token']->create($DI['api-app-acc-user']);
$DI['api-app-acc-user-not-admin'] = $this->container['manipulator.api-account']->create($DI['api-app-user-not-admin'], $DI['user_notAdmin']);
$this->container['manipulator.api-oauth-token']->create($DI['api-app-acc-user-not-admin']);
}
public function insertNativeApps()
{
$application = $this->container['manipulator.api-application']->create(
\API_OAuth2_Application_Navigator::CLIENT_NAME,
ApiApplication::DESKTOP_TYPE,
'',
'http://www.phraseanet.com',
null,
ApiApplication::NATIVE_APP_REDIRECT_URI
);
$application->setGrantPassword(true);
$application->setClientId(\API_OAuth2_Application_Navigator::CLIENT_ID);
$application->setClientSecret(\API_OAuth2_Application_Navigator::CLIENT_SECRET);
$this->container['manipulator.api-application']->update($application);
$application = $this->container['manipulator.api-application']->create(
\API_OAuth2_Application_OfficePlugin::CLIENT_NAME,
ApiApplication::DESKTOP_TYPE,
'',
'http://www.phraseanet.com',
null,
ApiApplication::NATIVE_APP_REDIRECT_URI
);
$application->setGrantPassword(true);
$application->setClientId(\API_OAuth2_Application_OfficePlugin::CLIENT_ID);
$application->setClientSecret(\API_OAuth2_Application_OfficePlugin::CLIENT_SECRET);
$this->container['manipulator.api-application']->update($application);
} }
private function insertAuthFailures(EntityManager $em, \Pimple $DI) private function insertAuthFailures(EntityManager $em, \Pimple $DI)

View File

@@ -1,393 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Controller\Api;
use Alchemy\Phrasea\Application;
class Logger
{
const DATABOXES_RESOURCE = 'databoxes';
const RECORDS_RESOURCE = 'record';
const BASKETS_RESOURCE = 'baskets';
const FEEDS_RESOURCE = 'feeds';
/**
*
* @var int
*/
private $id;
/**
*
* @var int
*/
private $account_id;
/**
*
* @var DateTime
*/
private $date;
/**
*
* @var int
*/
private $status_code;
/**
*
* @var string
*/
private $format;
/**
*
* @var string
*/
private $resource;
/**
*
* @var string
*/
private $general;
/**
*
* @var string
*/
private $aspect;
/**
*
* @var string
*/
private $action;
/**
*
* @var API_OAuth2_Account
*/
private $account;
/**
*
* @var Application
*/
private $app;
/**
*
* @param Application $app
* @param integer $log_id
*/
public function __construct(Application $app, $log_id)
{
$this->app = $app;
$this->id = (int) $log_id;
$sql = '
SELECT
api_log_id,
api_account_id,
api_log_route,
api_log_date,
api_log_status_code,
api_log_format,
api_log_resource,
api_log_general,
api_log_aspect,
api_log_action
FROM
api_logs
WHERE
api_log_id = :log_id';
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':log_id' => $this->id]);
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
$stmt->closeCursor();
$this->account_id = $row['api_account_id'];
$this->account = new \API_OAuth2_Account($this->app, (int) $row['api_account_id']);
$this->aspect = $row['api_log_aspect'];
$this->date = new \DateTime($row['api_log_date']);
$this->format = $row['api_log_format'];
$this->general = $row['api_log_general'];
$this->resource = $row['api_log_resource'];
$this->status_code = (int) $row['api_log_status_code'];
return $this;
}
public function get_account_id()
{
return $this->account_id;
}
public function set_account_id($account_id)
{
$this->account_id = $account_id;
$sql = 'UPDATE api_log
SET api_account_id = :account_id
WHERE api_log_id = :log_id';
$params = [
':api_account_id' => $this->account_id
, ':log_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
public function get_date()
{
return $this->date;
}
public function set_date(DateTime $date)
{
$this->date = $date;
$sql = 'UPDATE api_log
SET api_log_date = :date
WHERE api_log_id = :log_id';
$params = [
':date' => $this->date->format("Y-m-d H:i:s")
, ':log_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
public function get_status_code()
{
return $this->status_code;
}
public function set_status_code($status_code)
{
$this->status_code = (int) $status_code;
$sql = 'UPDATE api_log
SET api_log_status_code = :code
WHERE api_log_id = :log_id';
$params = [
':code' => $this->status_code
, ':log_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
public function get_format()
{
return $this->format;
}
public function set_format($format)
{
if ( ! in_array($format, ['json', 'jsonp', 'yaml', 'unknow']))
throw new \Exception_InvalidArgument();
$this->format = $format;
$sql = 'UPDATE api_log
SET api_log_format = :format
WHERE api_log_id = :log_id';
$params = [
':format' => $this->format
, ':log_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
public function get_resource()
{
return $this->resource;
}
public function set_resource($resource)
{
if ( ! in_array($resource, [self::DATABOXES_RESOURCE, self::BASKETS_RESOURCE, self::FEEDS_RESOURCE, self::RECORDS_RESOURCE]))
throw new \Exception_InvalidArgument();
$this->resource = $resource;
$sql = 'UPDATE api_log
SET api_log_resource = :resource
WHERE api_log_id = :log_id';
$params = [
':resource' => $this->resource,
':log_id' => $this->id,
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
public function get_general()
{
return $this->general;
}
public function set_general($general)
{
$this->general = $general;
$sql = 'UPDATE api_log
SET api_log_general = :general
WHERE api_log_id = :log_id';
$params = [
':general' => $this->general
, ':log_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
public function get_aspect()
{
return $this->aspect;
}
public function set_aspect($aspect)
{
$this->aspect = $aspect;
$sql = 'UPDATE api_log
SET api_log_aspect = :aspect
WHERE api_log_id = :log_id';
$params = [
':aspect' => $this->aspect
, ':log_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
public function get_action()
{
return $this->action;
}
public function set_action($action)
{
$this->action = $action;
$sql = 'UPDATE api_log
SET api_log_action = :action
WHERE api_log_id = :log_id';
$params = [
':action' => $this->action
, ':log_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
public function get_account()
{
return $this->account;
}
public static function create(Application $app, \API_OAuth2_Account $account, $route, $status_code, $format, $resource, $general = null, $aspect = null, $action = null)
{
$sql = '
INSERT INTO
api_logs (
api_log_id,
api_account_id,
api_log_route,
api_log_date,
api_log_status_code,
api_log_format,
api_log_resource,
api_log_general,
api_log_aspect,
api_log_action
)
VALUES (
null,
:account_id,
:route,
NOW(),
:status_code,
:format,
:resource,
:general,
:aspect,
:action
)';
$params = [
':account_id' => $account->get_id(),
':route' => $route,
':status_code' => $status_code,
':format' => $format,
':resource' => $resource,
':general' => $general,
':aspect' => $aspect,
':action' => $action
];
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$log_id = $app['phraseanet.appbox']->get_connection()->lastInsertId();
return new self($app, $log_id);
}
}

View File

@@ -21,6 +21,7 @@ use Silex\ControllerProviderInterface;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class Oauth2 implements ControllerProviderInterface class Oauth2 implements ControllerProviderInterface
{ {
@@ -30,10 +31,6 @@ class Oauth2 implements ControllerProviderInterface
$controllers = $app['controllers_factory']; $controllers = $app['controllers_factory'];
$app['oauth'] = $app->share(function ($app) {
return new \API_OAuth2_Adapter($app);
});
/** /**
* AUTHORIZE ENDPOINT * AUTHORIZE ENDPOINT
* *
@@ -42,45 +39,45 @@ class Oauth2 implements ControllerProviderInterface
*/ */
$authorize_func = function () use ($app) { $authorize_func = function () use ($app) {
$request = $app['request']; $request = $app['request'];
$oauth2_adapter = $app['oauth']; $oauth2Adapter = $app['oauth2-server'];
$context = new Context(Context::CONTEXT_OAUTH2_NATIVE); $context = new Context(Context::CONTEXT_OAUTH2_NATIVE);
$app['dispatcher']->dispatch(PhraseaEvents::PRE_AUTHENTICATE, new PreAuthenticate($request, $context)); $app['dispatcher']->dispatch(PhraseaEvents::PRE_AUTHENTICATE, new PreAuthenticate($request, $context));
//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 = $oauth2Adapter->getAuthorizationRequestParameters($request);
$app_authorized = false; $appAuthorized = false;
$errorMessage = false; $errorMessage = false;
$client = \API_OAuth2_Application::load_from_client_id($app, $params['client_id']); if (null === $client = $app['repo.api-applications']->findByClientId($params['client_id'])) {
throw new NotFoundHttpException(sprintf('Application with client id %s could not be found', $params['client_id']));
}
$oauth2_adapter->setClient($client); $oauth2Adapter->setClient($client);
$action_accept = $request->get("action_accept"); $actionAccept = $request->get("action_accept");
$action_login = $request->get("action_login"); $actionLogin = $request->get("action_login");
$template = "api/auth/end_user_authorization.html.twig"; $template = "api/auth/end_user_authorization.html.twig";
$custom_template = sprintf( $custom_template = sprintf(
"%s/config/templates/web/api/auth/end_user_authorization/%s.html.twig" "%s/config/templates/web/api/auth/end_user_authorization/%s.html.twig"
, $app['root.path'] , $app['root.path']
, $client->get_id() , $client->getId()
); );
if (file_exists($custom_template)) { if (file_exists($custom_template)) {
$template = sprintf( $template = sprintf(
'api/auth/end_user_authorization/%s.html.twig' 'api/auth/end_user_authorization/%s.html.twig'
, $client->get_id() , $client->getId()
); );
} }
if (!$app['authentication']->isAuthenticated()) { if (!$app['authentication']->isAuthenticated()) {
if ($action_login !== null) { if ($actionLogin !== null) {
try { try {
$usr_id = $app['auth.native']->getUsrId($request->get("login"), $request->get("password"), $request); if (null === $usrId = $app['auth.native']->getUsrId($request->get("login"), $request->get("password"), $request)) {
if (null === $usr_id) {
$app['session']->getFlashBag()->set('error', $app->trans('login::erreur: Erreur d\'authentification')); $app['session']->getFlashBag()->set('error', $app->trans('login::erreur: Erreur d\'authentification'));
return $app->redirectPath('oauth2_authorize'); return $app->redirectPath('oauth2_authorize');
@@ -91,48 +88,51 @@ class Oauth2 implements ControllerProviderInterface
return $app->redirectPath('oauth2_authorize', ['error' => 'account-locked']); return $app->redirectPath('oauth2_authorize', ['error' => 'account-locked']);
} }
$app['authentication']->openAccount($app['repo.users']->find($usr_id)); $app['authentication']->openAccount($app['repo.users']->find($usrId));
} }
return new Response($app['twig']->render($template, ["auth" => $oauth2_adapter])); return new Response($app['twig']->render($template, ["auth" => $oauth2Adapter]));
} }
//check if current client is already authorized by current user //check if current client is already authorized by current user
$user_auth_clients = \API_OAuth2_Application::load_authorized_app_by_user( $clients = $app['repo.api-applications']->findAuthorizedAppsByUser($app['authentication']->getUser());
$app
, $app['authentication']->getUser()
);
foreach ($user_auth_clients as $auth_client) { foreach ($clients as $authClient) {
if ($client->get_client_id() == $auth_client->get_client_id()) { if ($client->getClientId() == $authClient->getClientId()) {
$app_authorized = true; $appAuthorized = true;
break;
} }
} }
$account = $oauth2_adapter->updateAccount($app['authentication']->getUser()); $account = $oauth2Adapter->updateAccount($app['authentication']->getUser());
$params['account_id'] = $account->get_id(); $params['account_id'] = $account->getId();
if (!$app_authorized && $action_accept === null) { if (!$appAuthorized && $actionAccept === null) {
$params = [ $params = [
"auth" => $oauth2_adapter, "auth" => $oauth2Adapter,
"errorMessage" => $errorMessage, "errorMessage" => $errorMessage,
]; ];
return new Response($app['twig']->render($template, $params)); return new Response($app['twig']->render($template, $params));
} elseif (!$app_authorized && $action_accept !== null) { } elseif (!$appAuthorized && $actionAccept !== null) {
$app_authorized = (Boolean) $action_accept; $appAuthorized = (Boolean) $actionAccept;
$account->set_revoked(!$app_authorized);
if ($appAuthorized) {
$app['manipulator.api-account']->authorizeAccess($account);
} else {
$app['manipulator.api-account']->revokeAccess($account);
}
} }
//if native app show template //if native app show template
if ($oauth2_adapter->isNativeApp($params['redirect_uri'])) { if ($oauth2Adapter->isNativeApp($params['redirect_uri'])) {
$params = $oauth2_adapter->finishNativeClientAuthorization($app_authorized, $params); $params = $oauth2Adapter->finishNativeClientAuthorization($appAuthorized, $params);
return new Response($app['twig']->render("api/auth/native_app_access_token.html.twig", $params)); return new Response($app['twig']->render("api/auth/native_app_access_token.html.twig", $params));
} }
$oauth2_adapter->finishClientAuthorization($app_authorized, $params); $oauth2Adapter->finishClientAuthorization($appAuthorized, $params);
// As OAuth2 library already outputs response content, we need to send an empty // As OAuth2 library already outputs response content, we need to send an empty
// response to avoid breaking silex controller // response to avoid breaking silex controller
@@ -152,7 +152,7 @@ class Oauth2 implements ControllerProviderInterface
throw new HttpException(400, 'This route requires the use of the https scheme', null, ['content-type' => 'application/json']); throw new HttpException(400, 'This route requires the use of the https scheme', null, ['content-type' => 'application/json']);
} }
$app['oauth']->grantAccessToken($request); $app['oauth2-server']->grantAccessToken($request);
ob_flush(); ob_flush();
flush(); flush();

View File

@@ -62,8 +62,13 @@ class V1 implements ControllerProviderInterface
}); });
$controllers->after(function (Request $request, Response $response) use ($app) { $controllers->after(function (Request $request, Response $response) use ($app) {
$this->logQuery($app, $request, $response); $token = $app['session']->get('token');
$this->logout($app); $app['manipulator.api-log']->create($token->getAccount(), $request, $response);
$app['manipulator.api-oauth-token']->setLastUsed($token, new \DateTime());
$app['session']->set('token', null);
if (null !== $app['authentication']->getUser()) {
$app['authentication']->closeAccount();
}
}); });
$controllers->get('/monitor/scheduler/', 'controller.api.v1:get_scheduler') $controllers->get('/monitor/scheduler/', 'controller.api.v1:get_scheduler')
@@ -1967,119 +1972,39 @@ class V1 implements ControllerProviderInterface
$app['dispatcher']->dispatch(PhraseaEvents::PRE_AUTHENTICATE, new PreAuthenticate($request, $context)); $app['dispatcher']->dispatch(PhraseaEvents::PRE_AUTHENTICATE, new PreAuthenticate($request, $context));
$app['dispatcher']->dispatch(PhraseaEvents::API_OAUTH2_START, new ApiOAuth2StartEvent()); $app['dispatcher']->dispatch(PhraseaEvents::API_OAUTH2_START, new ApiOAuth2StartEvent());
$oauth2_adapter = new \API_OAuth2_Adapter($app); $app['oauth2-server']->verifyAccessToken();
$oauth2_adapter->verifyAccessToken();
$token = \API_OAuth2_Token::load_by_oauth_token($app, $oauth2_adapter->getToken()); if (null === $token = $app['repo.api-oauth-tokens']->find($app['oauth2-server']->getToken())) {
throw new NotFoundHttpException('Provided token is not valid.');
}
$app['session']->set('token', $token); $app['session']->set('token', $token);
$oAuth2App = $token->get_account()->get_application(); $oAuth2Account = $token->getAccount();
/* @var $oAuth2App \API_OAuth2_Application */ $oAuth2App = $oAuth2Account->getApplication();
if ($oAuth2App->get_client_id() == \API_OAuth2_Application_Navigator::CLIENT_ID if ($oAuth2App->getClientId() == \API_OAuth2_Application_Navigator::CLIENT_ID
&& !$app['conf']->get(['registry', 'api-clients', 'navigator-enabled'])) { && !$app['conf']->get(['registry', 'api-clients', 'navigator-enabled'])) {
return Result::createError($request, 403, 'The use of phraseanet Navigator is not allowed')->createResponse(); return Result::createError($request, 403, 'The use of Phraseanet Navigator is not allowed')->createResponse();
} }
if ($oAuth2App->get_client_id() == \API_OAuth2_Application_OfficePlugin::CLIENT_ID if ($oAuth2App->getClientId() == \API_OAuth2_Application_OfficePlugin::CLIENT_ID
&& ! $app['conf']->get(['registry', 'api-clients', 'office-enabled'])) { && ! $app['conf']->get(['registry', 'api-clients', 'office-enabled'])) {
return Result::createError($request, 403, 'The use of Office Plugin is not allowed.')->createResponse(); return Result::createError($request, 403, 'The use of Office Plugin is not allowed.')->createResponse();
} }
$user = $app['repo.users']->find($oauth2_adapter->get_usr_id()); $app['authentication']->openAccount($oAuth2Account->getUser());
$app['authentication']->openAccount($user); $app['oauth2-server']->rememberSession($app['session']);
$oauth2_adapter->remember_this_ses_id($app['session']->get('session_id'));
$app['dispatcher']->dispatch(PhraseaEvents::API_OAUTH2_END, new ApiOAuth2EndEvent()); $app['dispatcher']->dispatch(PhraseaEvents::API_OAUTH2_END, new ApiOAuth2EndEvent());
} }
public function ensureAdmin(Request $request, Application $app) public function ensureAdmin(Request $request, Application $app)
{ {
$user = $app['session']->get('token')->get_account()->get_user(); $user = $app['session']->get('token')->getAccount()->getUser();
if (!$app['acl']->get($user)->is_admin()) { if (!$user->isAdmin()) {
return Result::createError($request, 401, 'You are not authorized')->createResponse(); return Result::createError($request, 401, 'You are not authorized')->createResponse();
} }
} }
private function logQuery(Application $app, Request $request, Response $response)
{
$infos = $this->parseRoute($request->getPathInfo(), $response);
Logger::create(
$app,
$app['session']->get('token')->get_account(),
$request->getMethod() . " " . $request->getPathInfo(),
$response->getStatusCode(),
$response->headers->get('content-type'),
$infos['ressource'],
$infos['general'],
$infos['aspect'],
$infos['action']
);
}
/**
* Parses the requested route to fetch
* - the ressource (databox, basket, record etc ..)
* - general action (list, add, search)
* - the action (setstatus, setname etc..)
* - the aspect (collections, related, content etc..)
*
* @return array
*/
private function parseRoute($route, Response $response)
{
$ressource = $general = $aspect = $action = null;
$exploded_route = explode('/', trim($route, '/'));
if ($response->isOk() && sizeof($exploded_route) > 0) {
$ressource = $exploded_route[0];
if (count($exploded_route) == 2 && (int) $exploded_route[1] == 0) {
$general = $exploded_route[1];
} else {
switch ($ressource) {
case Logger::DATABOXES_RESOURCE :
if ((int) $exploded_route[1] > 0 && count($exploded_route) == 3) {
$aspect = $exploded_route[2];
}
break;
case Logger::RECORDS_RESOURCE :
if ((int) $exploded_route[1] > 0 && count($exploded_route) == 4) {
if (!isset($exploded_route[3])) {
$aspect = "record";
} elseif (preg_match("/^set/", $exploded_route[3])) {
$action = $exploded_route[3];
} else {
$aspect = $exploded_route[3];
}
}
break;
case Logger::BASKETS_RESOURCE :
if ((int) $exploded_route[1] > 0 && count($exploded_route) == 3) {
if (preg_match("/^set/", $exploded_route[2]) || preg_match("/^delete/", $exploded_route[2])) {
$action = $exploded_route[2];
} else {
$aspect = $exploded_route[2];
}
}
break;
case Logger::FEEDS_RESOURCE :
if ((int) $exploded_route[1] > 0 && count($exploded_route) == 3) {
$aspect = $exploded_route[2];
}
break;
}
}
}
return [
'ressource' => $ressource,
'general' => $general,
'aspect' => $aspect,
'action' => $action
];
}
private function list_user(User $user) private function list_user(User $user)
{ {
switch ($user->getGender()) { switch ($user->getGender()) {
@@ -2119,11 +2044,4 @@ class V1 implements ControllerProviderInterface
'locale' => $user->getLocale() ?: null, 'locale' => $user->getLocale() ?: null,
]; ];
} }
private function logout(Application $app)
{
if (null !== $app['authentication']->getUser()) {
$app['authentication']->closeAccount();
}
}
} }

View File

@@ -15,6 +15,7 @@ use Alchemy\Geonames\Exception\ExceptionInterface as GeonamesExceptionInterface;
use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Application as PhraseaApplication;
use Alchemy\Phrasea\Exception\InvalidArgumentException; use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Entities\FtpCredential; use Alchemy\Phrasea\Model\Entities\FtpCredential;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Notification\Receiver; use Alchemy\Phrasea\Notification\Receiver;
use Alchemy\Phrasea\Notification\Mail\MailRequestEmailUpdate; use Alchemy\Phrasea\Notification\Mail\MailRequestEmailUpdate;
use Alchemy\Phrasea\Form\Login\PhraseaRenewPasswordForm; use Alchemy\Phrasea\Form\Login\PhraseaRenewPasswordForm;
@@ -69,7 +70,8 @@ class Account implements ControllerProviderInterface
->bind('account_auth_apps'); ->bind('account_auth_apps');
// Displays a an authorized app grant // Displays a an authorized app grant
$controllers->get('/security/application/{application_id}/grant/', 'account.controller:grantAccess') $controllers->get('/security/application/{application}/grant/', 'account.controller:grantAccess')
->before($app['middleware.api-application.converter'])
->assert('application_id', '\d+') ->assert('application_id', '\d+')
->bind('grant_app_access'); ->bind('grant_app_access');
@@ -191,33 +193,29 @@ class Account implements ControllerProviderInterface
/** /**
* Display authorized applications that can access user informations * Display authorized applications that can access user informations
* *
* @param Application $app A Silex application where the controller is mounted on * @param Application $app
* @param Request $request The current request * @param Request $request
* @param Integer $application_id The application id * @param ApiApplication $application
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function grantAccess(Application $app, Request $request, $application_id) public function grantAccess(Application $app, Request $request, ApiApplication $application)
{ {
if (!$request->isXmlHttpRequest() || !array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) { if (!$request->isXmlHttpRequest() || !array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) {
$app->abort(400, $app->trans('Bad request format, only JSON is allowed')); $app->abort(400, $app->trans('Bad request format, only JSON is allowed'));
} }
$error = false; if (null === $account = $app['repo.api-accounts']->findByUserAndApplication($app['authentication']->getUser(), $application)) {
return $app->json(['success' => false]);
try {
$account = \API_OAuth2_Account::load_with_user(
$app
, new \API_OAuth2_Application($app, $application_id)
, $app['authentication']->getUser()
);
$account->set_revoked((bool) $request->query->get('revoke'), false);
} catch (NotFoundHttpException $e) {
$error = true;
} }
return $app->json(['success' => !$error]); if (false === (Boolean) $request->query->get('revoke')) {
$app['manipulator.api-account']->authorizeAccess($account);
} else {
$app['manipulator.api-account']->revokeAccess($account);
}
return $app->json(['success' => true]);
} }
/** /**
@@ -243,8 +241,17 @@ class Account implements ControllerProviderInterface
*/ */
public function accountAuthorizedApps(Application $app, Request $request) public function accountAuthorizedApps(Application $app, Request $request)
{ {
$data = [];
foreach ($app['repo.api-applications']->findByUser($app['authentication']->getUser()) as $application) {
$account = $app['repo.api-accounts']->findByUserAndApplication($app['authentication']->getUser(), $application);
$data[$application->getId()]['application'] = $application;
$data[$application->getId()]['user-account'] = $account;
}
return $app['twig']->render('account/authorized_apps.html.twig', [ return $app['twig']->render('account/authorized_apps.html.twig', [
"applications" => \API_OAuth2_Application::load_app_by_user($app, $app['authentication']->getUser()), "applications" => $data,
]); ]);
} }

View File

@@ -11,6 +11,8 @@
namespace Alchemy\Phrasea\Controller\Root; namespace Alchemy\Phrasea\Controller\Root;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Silex\Application; use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
@@ -37,147 +39,126 @@ class Developers implements ControllerProviderInterface
$controllers->post('/application/', 'controller.account.developers:newApp') $controllers->post('/application/', 'controller.account.developers:newApp')
->bind('submit_developers_application'); ->bind('submit_developers_application');
$controllers->get('/application/{id}/', 'controller.account.developers:getApp') $controllers->get('/application/{application}/', 'controller.account.developers:getApp')
->assert('id', '\d+') ->before($app['middleware.api-application.converter'])
->assert('application', '\d+')
->bind('developers_application'); ->bind('developers_application');
$controllers->delete('/application/{id}/', 'controller.account.developers:deleteApp') $controllers->delete('/application/{application}/', 'controller.account.developers:deleteApp')
->assert('id', '\d+') ->before($app['middleware.api-application.converter'])
->assert('application', '\d+')
->bind('delete_developers_application'); ->bind('delete_developers_application');
$controllers->post('/application/{id}/authorize_grant_password/', 'controller.account.developers:authorizeGrantpassword') $controllers->post('/application/{application}/authorize_grant_password/', 'controller.account.developers:authorizeGrantPassword')
->assert('id', '\d+') ->before($app['middleware.api-application.converter'])
->assert('application', '\d+')
->bind('submit_developers_application_authorize_grant_password'); ->bind('submit_developers_application_authorize_grant_password');
$controllers->post('/application/{id}/access_token/', 'controller.account.developers:renewAccessToken') $controllers->post('/application/{application}/access_token/', 'controller.account.developers:renewAccessToken')
->assert('id', '\d+') ->before($app['middleware.api-application.converter'])
->assert('application', '\d+')
->bind('submit_developers_application_token'); ->bind('submit_developers_application_token');
$controllers->post('/application/{id}/callback/', 'controller.account.developers:renewAppCallback') $controllers->post('/application/{application}/callback/', 'controller.account.developers:renewAppCallback')
->assert('id', '\d+') ->before($app['middleware.api-application.converter'])
->assert('application', '\d+')
->bind('submit_application_callback'); ->bind('submit_application_callback');
return $controllers; return $controllers;
} }
/** /**
* Delete application * Delete application.
*
* @param Application $app
* @param Request $request
* @param ApiApplication $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 JsonResponse * @return JsonResponse
*/ */
public function deleteApp(Application $app, Request $request, $id) public function deleteApp(Application $app, Request $request, ApiApplication $application)
{ {
if (!$request->isXmlHttpRequest() || !array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) { if (!$request->isXmlHttpRequest() || !array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) {
$app->abort(400, 'Bad request format, only JSON is allowed'); $app->abort(400, 'Bad request format, only JSON is allowed');
} }
$error = false; $app['manipulator.api-application']->delete($application);
try { return $app->json(['success' => true]);
$clientApp = new \API_OAuth2_Application($app, $id);
$clientApp->delete();
} catch (NotFoundHttpException $e) {
$error = true;
}
return $app->json(['success' => !$error]);
} }
/** /**
* Change application callback * Change application callback.
*
* @param Application $app
* @param Request $request
* @param ApiApplication $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 JsonResponse * @return JsonResponse
*/ */
public function renewAppCallback(Application $app, Request $request, $id) public function renewAppCallback(Application $app, Request $request, ApiApplication $application)
{ {
if (!$request->isXmlHttpRequest() || !array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) { if (!$request->isXmlHttpRequest() || !array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) {
$app->abort(400, 'Bad request format, only JSON is allowed'); $app->abort(400, 'Bad request format, only JSON is allowed');
} }
$error = false;
try { try {
$clientApp = new \API_OAuth2_Application($app, $id); $app['manipulator.api-application']->setRedirectUri($application, $request->request->get("callback"));
} catch (InvalidArgumentException $e) {
if (null !== $request->request->get("callback")) { return $app->json(['success' => false]);
$clientApp->set_redirect_uri($request->request->get("callback"));
} else {
$error = true;
}
} catch (NotFoundHttpException $e) {
$error = true;
} }
return $app->json(['success' => !$error]); return $app->json(['success' => true]);
} }
/** /**
* Authorize application to use a grant password type * Authorize application to use a grant password type.
*
* @param Application $app
* @param Request $request
* @param ApiApplication $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 JsonResponse * @return JsonResponse
*/ */
public function renewAccessToken(Application $app, Request $request, $id) public function renewAccessToken(Application $app, Request $request, ApiApplication $application)
{ {
if (!$request->isXmlHttpRequest() || !array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) { if (!$request->isXmlHttpRequest() || !array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) {
$app->abort(400, 'Bad request format, only JSON is allowed'); $app->abort(400, 'Bad request format, only JSON is allowed');
} }
$error = false; if (null === $account = $app['repo.api-accounts']->findByUserAndApplication($app['authentication']->getUser(), $application)) {
$accessToken = null; $app->abort(404, sprintf('Account not found for application %s', $application->getName()));
try {
$clientApp = new \API_OAuth2_Application($app, $id);
$account = $clientApp->get_user_account($app['authentication']->getUser());
$token = $account->get_token();
if ($token instanceof \API_OAuth2_Token) {
$token->renew();
} else {
$token = \API_OAuth2_Token::create($app['phraseanet.appbox'], $account, $app['random.medium']);
}
$accessToken = $token->get_value();
} catch (\Exception $e) {
$error = true;
} }
return $app->json(['success' => !$error, 'token' => $accessToken]); if(null !== $devToken = $app['repo.api-oauth-tokens']->findDeveloperToken($account)) {
$app['manipulator.api-oauth-token']->renew($devToken);
} else {
// dev tokens do not expires
$devToken = $app['manipulator.api-oauth-token']->create($account);
}
return $app->json(['success' => true, 'token' => $devToken->getOauthToken()]);
} }
/** /**
* Authorize application to use a grant password type * Authorize application to use a grant password type.
*
* @param Application $app
* @param Request $request
* @param ApiApplication $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 JsonResponse * @return JsonResponse
*/ */
public function authorizeGrantpassword(Application $app, Request $request, $id) public function authorizeGrantPassword(Application $app, Request $request, ApiApplication $application)
{ {
if (!$request->isXmlHttpRequest() || !array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) { if (!$request->isXmlHttpRequest() || !array_key_exists($request->getMimeType('json'), array_flip($request->getAcceptableContentTypes()))) {
$app->abort(400, 'Bad request format, only JSON is allowed'); $app->abort(400, 'Bad request format, only JSON is allowed');
} }
$error = false; $application->setGrantPassword((Boolean) $request->request->get('grant'));
$app['manipulator.api-application']->update($application);
try { return $app->json(['success' => true]);
$clientApp = new \API_OAuth2_Application($app, $id);
$clientApp->set_grant_password((bool) $request->request->get('grant', false));
} catch (NotFoundHttpException $e) {
$error = true;
}
return $app->json(['success' => !$error]);
} }
/** /**
@@ -189,7 +170,7 @@ class Developers implements ControllerProviderInterface
*/ */
public function newApp(Application $app, Request $request) public function newApp(Application $app, Request $request)
{ {
if ($request->request->get('type') === \API_OAuth2_Application::DESKTOP_TYPE) { if ($request->request->get('type') === ApiApplication::DESKTOP_TYPE) {
$form = new \API_OAuth2_Form_DevAppDesktop($app['request']); $form = new \API_OAuth2_Form_DevAppDesktop($app['request']);
} else { } else {
$form = new \API_OAuth2_Form_DevAppInternet($app['request']); $form = new \API_OAuth2_Form_DevAppInternet($app['request']);
@@ -198,22 +179,25 @@ class Developers implements ControllerProviderInterface
$violations = $app['validator']->validate($form); $violations = $app['validator']->validate($form);
if ($violations->count() === 0) { if ($violations->count() === 0) {
$application = \API_OAuth2_Application::create($app, $app['authentication']->getUser(), $form->getName()); $application = $app['manipulator.api-application']->create(
$application $form->getName(),
->set_description($form->getDescription()) $form->getType(),
->set_redirect_uri($form->getSchemeCallback() . $form->getCallback()) $form->getDescription(),
->set_type($form->getType()) sprintf('%s%s', $form->getSchemeWebsite(), $form->getWebsite()),
->set_website($form->getSchemeWebsite() . $form->getWebsite()); $app['authentication']->getUser(),
sprintf('%s%s', $form->getSchemeCallback(), $form->getCallback())
);
return $app->redirectPath('developers_application', ['id' => $application->get_id()]); // create an account as well
$app['manipulator.api-account']->create($application, $app['authentication']->getUser());
return $app->redirectPath('developers_application', ['application' => $application->getId()]);
} }
$var = [ return $app['twig']->render('/developers/application_form.html.twig', [
"violations" => $violations, "violations" => $violations,
"form" => $form "form" => $form
]; ]);
return $app['twig']->render('/developers/application_form.html.twig', $var);
} }
/** /**
@@ -226,7 +210,7 @@ class Developers implements ControllerProviderInterface
public function listApps(Application $app, Request $request) public function listApps(Application $app, Request $request)
{ {
return $app['twig']->render('developers/applications.html.twig', [ return $app['twig']->render('developers/applications.html.twig', [
"applications" => \API_OAuth2_Application::load_dev_app_by_user($app, $app['authentication']->getUser()) "applications" => $app['repo.api-applications']->findByCreator($app['authentication']->getUser())
]); ]);
} }
@@ -247,25 +231,24 @@ class Developers implements ControllerProviderInterface
} }
/** /**
* Get application information * Gets application information.
* *
* @param Application $app A Silex application where the controller is mounted on * @param Application $app
* @param Request $request The current request * @param Request $request
* @param integer $id The application id * @param ApiApplication $application
* @return Response *
* @return mixed
*/ */
public function getApp(Application $app, Request $request, $id) public function getApp(Application $app, Request $request, ApiApplication $application)
{ {
try { $token = null;
$client = new \API_OAuth2_Application($app, $id);
} catch (NotFoundHttpException $e) { if (null !== $account = $app['repo.api-accounts']->findByUserAndApplication($app['authentication']->getUser(), $application)) {
$app->abort(404); $token = $app['repo.api-oauth-tokens']->findDeveloperToken($account);
} }
$token = $client->get_user_account($app['authentication']->getUser())->get_token()->get_value();
return $app['twig']->render('developers/application.html.twig', [ return $app['twig']->render('developers/application.html.twig', [
"application" => $client, "application" => $application,
"user" => $app['authentication']->getUser(), "user" => $app['authentication']->getUser(),
"token" => $token "token" => $token
]); ]);

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Event;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Model\Entities\User;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class InstallFinishEvent extends SfEvent
{
private $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function getUser()
{
return $this->user;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Event\Subscriber;
use Alchemy\Phrasea\Core\Event\InstallFinishEvent;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Silex\Application;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class PhraseaInstallSubscriber implements EventSubscriberInterface
{
private $app;
public function __construct(Application $app)
{
$this->app = $app;
}
public static function getSubscribedEvents()
{
return [
PhraseaEvents::INSTALL_FINISH => 'onInstallFinished'
];
}
public function onInstallFinished(InstallFinishEvent $event)
{
$this->createNavigatorApplication();
$this->createOfficePluginApplication();
}
private function createNavigatorApplication()
{
$application = $this->app['manipulator.api-application']->create(
\API_OAuth2_Application_Navigator::CLIENT_NAME,
ApiApplication::DESKTOP_TYPE,
'',
'http://www.phraseanet.com',
null,
ApiApplication::NATIVE_APP_REDIRECT_URI
);
$application->setGrantPassword(true);
$application->setClientId(\API_OAuth2_Application_Navigator::CLIENT_ID);
$application->setClientSecret(\API_OAuth2_Application_Navigator::CLIENT_SECRET);
$this->app['manipulator.api-application']->update($application);
}
private function createOfficePluginApplication()
{
$application = $this->app['manipulator.api-application']->create(
\API_OAuth2_Application_OfficePlugin::CLIENT_NAME,
ApiApplication::DESKTOP_TYPE,
'',
'http://www.phraseanet.com',
null,
ApiApplication::NATIVE_APP_REDIRECT_URI
);
$application->setGrantPassword(true);
$application->setClientId(\API_OAuth2_Application_OfficePlugin::CLIENT_ID);
$application->setClientSecret(\API_OAuth2_Application_OfficePlugin::CLIENT_SECRET);
$this->app['manipulator.api-application']->update($application);
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Middleware;
use Silex\Application;
use Silex\ServiceProviderInterface;
use Symfony\Component\HttpFoundation\Request;
class ApiApplicationMiddlewareProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['middleware.api-application.converter'] = $app->protect(function (Request $request, Application $app) {
if ($request->attributes->has('application')) {
$request->attributes->set('application', $app['converter.api-application']->convert($request->attributes->get('application')));
}
});
}
public function boot(Application $app)
{
}
}

View File

@@ -18,6 +18,8 @@ final class PhraseaEvents
const PRE_AUTHENTICATE = 'phrasea.pre-authenticate'; const PRE_AUTHENTICATE = 'phrasea.pre-authenticate';
const POST_AUTHENTICATE = 'phrasea.post-authenticate'; const POST_AUTHENTICATE = 'phrasea.post-authenticate';
const INSTALL_FINISH = "phrasea.install-finish";
const API_OAUTH2_START = 'api.oauth2.start'; const API_OAUTH2_START = 'api.oauth2.start';
const API_OAUTH2_END = 'api.oauth2.end'; const API_OAUTH2_END = 'api.oauth2.end';
const API_LOAD_START = 'api.load.start'; const API_LOAD_START = 'api.load.start';

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Provider;
use Silex\Application;
use Silex\ServiceProviderInterface;
class APIServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['oauth2-server'] = $app->share(function ($app) {
return new \API_OAuth2_Adapter($app);
});
}
public function boot(Application $app)
{
}
}

View File

@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\Core\Provider; namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Model\Converter\ApiApplicationConverter;
use Alchemy\Phrasea\Model\Converter\BasketConverter; use Alchemy\Phrasea\Model\Converter\BasketConverter;
use Alchemy\Phrasea\Model\Converter\TaskConverter; use Alchemy\Phrasea\Model\Converter\TaskConverter;
use Alchemy\Phrasea\Model\Converter\TokenConverter; use Alchemy\Phrasea\Model\Converter\TokenConverter;
@@ -32,6 +33,10 @@ class ConvertersServiceProvider implements ServiceProviderInterface
$app['converter.token'] = $app->share(function ($app) { $app['converter.token'] = $app->share(function ($app) {
return new TokenConverter($app['repo.tokens']); return new TokenConverter($app['repo.tokens']);
}); });
$app['converter.api-application'] = $app->share(function ($app) {
return new ApiApplicationConverter($app['repo.api-applications']);
});
} }
public function boot(Application $app) public function boot(Application $app)

View File

@@ -13,6 +13,12 @@ namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Model\Manipulator\ACLManipulator; use Alchemy\Phrasea\Model\Manipulator\ACLManipulator;
use Alchemy\Phrasea\Model\Manipulator\PresetManipulator; use Alchemy\Phrasea\Model\Manipulator\PresetManipulator;
use Alchemy\Phrasea\Model\Manipulator\ApiAccountManipulator;
use Alchemy\Phrasea\Model\Manipulator\ApiApplicationManipulator;
use Alchemy\Phrasea\Model\Manipulator\ApiLogManipulator;
use Alchemy\Phrasea\Model\Manipulator\ApiOauthCodeManipulator;
use Alchemy\Phrasea\Model\Manipulator\ApiOauthRefreshTokenManipulator;
use Alchemy\Phrasea\Model\Manipulator\ApiOauthTokenManipulator;
use Alchemy\Phrasea\Model\Manipulator\RegistrationManipulator; use Alchemy\Phrasea\Model\Manipulator\RegistrationManipulator;
use Alchemy\Phrasea\Model\Manipulator\TaskManipulator; use Alchemy\Phrasea\Model\Manipulator\TaskManipulator;
use Alchemy\Phrasea\Model\Manipulator\TokenManipulator; use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
@@ -52,6 +58,30 @@ class ManipulatorServiceProvider implements ServiceProviderInterface
$app['manipulator.registration'] = $app->share(function ($app) { $app['manipulator.registration'] = $app->share(function ($app) {
return new RegistrationManipulator($app, $app['EM'], $app['acl'], $app['phraseanet.appbox'], $app['repo.registrations']); return new RegistrationManipulator($app, $app['EM'], $app['acl'], $app['phraseanet.appbox'], $app['repo.registrations']);
}); });
$app['manipulator.api-application'] = $app->share(function ($app) {
return new ApiApplicationManipulator($app['EM'], $app['repo.api-applications'], $app['random.medium']);
});
$app['manipulator.api-account'] = $app->share(function ($app) {
return new ApiAccountManipulator($app['EM'], $app['repo.api-accounts']);
});
$app['manipulator.api-oauth-code'] = $app->share(function ($app) {
return new ApiOauthCodeManipulator($app['EM'], $app['repo.api-oauth-codes'], $app['random.medium']);
});
$app['manipulator.api-oauth-token'] = $app->share(function ($app) {
return new ApiOauthTokenManipulator($app['EM'], $app['repo.api-oauth-tokens'], $app['random.medium']);
});
$app['manipulator.api-oauth-refresh-token'] = $app->share(function ($app) {
return new ApiOauthRefreshTokenManipulator($app['EM'], $app['repo.api-oauth-refresh-tokens'], $app['random.medium']);
});
$app['manipulator.api-log'] = $app->share(function ($app) {
return new ApiLogManipulator($app['EM'], $app['repo.api-logs']);
});
} }
public function boot(SilexApplication $app) public function boot(SilexApplication $app)

View File

@@ -97,6 +97,24 @@ class RepositoriesServiceProvider implements ServiceProviderInterface
$app['repo.presets'] = $app->share(function (PhraseaApplication $app) { $app['repo.presets'] = $app->share(function (PhraseaApplication $app) {
return $app['EM']->getRepository('Phraseanet:Preset'); return $app['EM']->getRepository('Phraseanet:Preset');
}); });
$app['repo.api-accounts'] = $app->share(function (PhraseaApplication $app) {
return $app['EM']->getRepository('Phraseanet:ApiAccount');
});
$app['repo.api-logs'] = $app->share(function (PhraseaApplication $app) {
return $app['EM']->getRepository('Phraseanet:ApiLog');
});
$app['repo.api-applications'] = $app->share(function (PhraseaApplication $app) {
return $app['EM']->getRepository('Phraseanet:ApiApplication');
});
$app['repo.api-oauth-codes'] = $app->share(function (PhraseaApplication $app) {
return $app['EM']->getRepository('Phraseanet:ApiOauthCode');
});
$app['repo.api-oauth-tokens'] = $app->share(function (PhraseaApplication $app) {
return $app['EM']->getRepository('Phraseanet:ApiOauthToken');
});
$app['repo.api-oauth-refresh-tokens'] = $app->share(function (PhraseaApplication $app) {
return $app['EM']->getRepository('Phraseanet:ApiOauthRefreshToken');
});
} }
public function boot(Application $app) public function boot(Application $app)

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Converter;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Model\Repositories\ApiApplicationRepository;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class ApiApplicationConverter implements ConverterInterface
{
private $repository;
public function __construct(ApiApplicationRepository $repository)
{
$this->repository = $repository;
}
/**
* {@inheritdoc}
*
* @return ApiApplication
*/
public function convert($id)
{
if (null === $application = $this->repository->find((int) $id)) {
throw new NotFoundHttpException(sprintf('Application %s not found.', $id));
}
return $application;
}
}

View File

@@ -0,0 +1,192 @@
<?php
namespace Alchemy\Phrasea\Model\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @ORM\Table(name="ApiAccounts", indexes={
* @ORM\Index(name="user_id", columns={"user_id"}),
* @ORM\Index(name="application_id", columns={"application_id"})
* })
* @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\ApiAccountRepository")
*/
class ApiAccount
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="User")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*
* @return User
**/
private $user;
/**
* @var integer
*
* @ORM\Column(type="boolean")
*/
private $revoked = false;
/**
* @var string
*
* @ORM\Column(name="api_version", type="string", length=16, nullable=false)
*/
private $apiVersion;
/**
* @ORM\ManyToOne(targetEntity="ApiApplication", inversedBy="accounts")
* @ORM\JoinColumn(name="application_id", referencedColumnName="id", nullable=false)
*
* @return ApiApplication
**/
private $application;
/**
* @ORM\OneToMany(targetEntity="ApiOauthToken", mappedBy="account", cascade={"remove"})
**/
private $tokens;
/**
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created;
public function __construct()
{
$this->tokens = new ArrayCollection();
}
/**
* @param string $apiVersion
*
* @return ApiAccount
*/
public function setApiVersion($apiVersion)
{
$this->apiVersion = $apiVersion;
return $this;
}
/**
* @return string
*/
public function getApiVersion()
{
return $this->apiVersion;
}
/**
* @param ApiApplication $application
*
* @return ApiAccount
*/
public function setApplication(ApiApplication $application)
{
$application->addAccount($this);
$this->application = $application;
return $this;
}
/**
* @return ApiApplication
*/
public function getApplication()
{
return $this->application;
}
/**
* @param \DateTime $created
*
* @return ApiAccount
*/
public function setCreated(\DateTime $created)
{
$this->created = $created;
return $this;
}
/**
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* @param boolean $revoked
*
* @return ApiAccount
*/
public function setRevoked($revoked)
{
$this->revoked = (Boolean) $revoked;
return $this;
}
/**
* @return boolean
*/
public function isRevoked()
{
return $this->revoked;
}
/**
* @param User $user
*
* @return ApiAccount
*/
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
/**
* @return User
*/
public function getUser()
{
return $this->user;
}
/**
* @param ApiOauthToken $token
*
* @return $this
*/
public function addTokens(ApiOauthToken $token)
{
$this->tokens->add($token);
return $this;
}
}

View File

@@ -0,0 +1,412 @@
<?php
namespace Alchemy\Phrasea\Model\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @ORM\Table(name="ApiApplications", uniqueConstraints={
* @ORM\UniqueConstraint(name="client_id", columns={"client_id"})}, indexes={
* @ORM\Index(name="creator_id", columns={"creator_id"})
* })
* @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\ApiApplicationRepository")
*/
class ApiApplication
{
/** desktop application */
const DESKTOP_TYPE = 'desktop';
/** web application */
const WEB_TYPE = 'web';
/** Uniform Resource Name */
const NATIVE_APP_REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob";
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="User")
* @ORM\JoinColumn(name="creator_id", referencedColumnName="id", nullable=true)
*
* @return User|null
**/
private $creator;
/**
* @var string
*
* @ORM\Column(type="string", length=128, nullable=false)
*/
private $type;
/**
* @var string
*
* @ORM\Column(type="string", length=128, nullable=false)
*/
private $name;
/**
* @var string
*
* @ORM\Column(type="text", nullable=false)
*/
private $description;
/**
* @var string
*
* @ORM\Column(type="string", length=128, nullable=false)
*/
private $website;
/**
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created;
/**
* @Gedmo\Timestampable(on="update")
* @ORM\Column(type="datetime")
*/
private $updated;
/**
* @var string
*
* @ORM\Column(name="client_id", type="string", length=32, nullable=false)
*/
private $clientId;
/**
* @var string
*
* @ORM\Column(name="client_secret", type="string", length=32, nullable=false)
*/
private $clientSecret;
/**
* @var string
*
* @ORM\Column(type="string", length=64, nullable=false)
*/
private $nonce;
/**
* @var string
*
* @ORM\Column(name="redirect_uri", type="string", length=128, nullable=false)
*/
private $redirectUri;
/**
* @var integer
*
* @ORM\Column(type="boolean", nullable=false)
*/
private $activated = true;
/**
* @var integer
*
* @ORM\Column(name="grant_password", type="boolean", nullable=false)
*/
private $grantPassword = false;
/**
* @ORM\OneToMany(targetEntity="ApiAccount", mappedBy="application", cascade={"remove"})
**/
private $accounts;
public function __construct()
{
$this->accounts = new ArrayCollection();
}
/**
* @param boolean $activated
*
* @return ApiApplication
*/
public function setActivated($activated)
{
$this->activated = (Boolean) $activated;
return $this;
}
/**
* @return boolean
*/
public function isActivated()
{
return $this->activated;
}
/**
* @param string $clientId
*
* @return ApiApplication
*/
public function setClientId($clientId)
{
$this->clientId = $clientId;
return $this;
}
/**
* @return string
*/
public function getClientId()
{
return $this->clientId;
}
/**
* @param string $clientSecret
*
* @return ApiApplication
*/
public function setClientSecret($clientSecret)
{
$this->clientSecret = $clientSecret;
return $this;
}
/**
* @return string
*/
public function getClientSecret()
{
return $this->clientSecret;
}
/**
* @param \DateTime $created
*
* @return ApiApplication
*/
public function setCreated(\DateTime $created)
{
$this->created = $created;
return $this;
}
/**
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* @param User $creator
*
* @return ApiApplication
*/
public function setCreator(User $creator = null)
{
$this->creator = $creator;
return $this;
}
/**
* @return User|null
*/
public function getCreator()
{
return $this->creator;
}
/**
* @param string $description
*
* @return ApiApplication
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* @param boolean $grantPassword
*
* @return ApiApplication
*/
public function setGrantPassword($grantPassword)
{
$this->grantPassword = (Boolean) $grantPassword;
return $this;
}
/**
* @return boolean
*/
public function isPasswordGranted()
{
return $this->grantPassword;
}
/**
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* @param string $name
*
* @return ApiApplication
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $nonce
*
* @return ApiApplication
*/
public function setNonce($nonce)
{
$this->nonce = $nonce;
return $this;
}
/**
* @return string
*/
public function getNonce()
{
return $this->nonce;
}
/**
* @param string $redirectUri
*
* @return ApiApplication
*/
public function setRedirectUri($redirectUri)
{
$this->redirectUri = $redirectUri;
return $this;
}
/**
* @return string
*/
public function getRedirectUri()
{
return $this->redirectUri;
}
/**
* @param string $type
*
* @return ApiApplication
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* @param \DateTime $updated
*
* @return ApiApplication
*/
public function setUpdated(\DateTime $updated)
{
$this->updated = $updated;
return $this;
}
/**
* @return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* @param string $website
*
* @return ApiApplication
*/
public function setWebsite($website)
{
$this->website = $website;
return $this;
}
/**
* @return string
*/
public function getWebsite()
{
return $this->website;
}
/**
* @param ApiAccount $account
*
* @return $this
*/
public function addAccount(ApiAccount $account)
{
$this->accounts->add($account);
return $this;
}
}

View File

@@ -0,0 +1,360 @@
<?php
namespace Alchemy\Phrasea\Model\Entities;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @ORM\Table(name="ApiLogs", indexes={@ORM\Index(name="account_id", columns={"account_id"})})
* @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\ApiLogRepository")
*/
class ApiLog
{
const DATABOXES_RESOURCE = 'databoxes';
const RECORDS_RESOURCE = 'records';
const BASKETS_RESOURCE = 'baskets';
const FEEDS_RESOURCE = 'feeds';
const QUARANTINE_RESOURCE = 'quarantine';
const STORIES_RESOURCE = 'stories';
const MONITOR_RESOURCE = 'monitor';
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="ApiAccount")
* @ORM\JoinColumn(name="account_id", referencedColumnName="id", nullable=false)
*
* @return ApiAccount
**/
private $account;
/**
* @var string
*
* @ORM\Column(type="string", length=128, nullable=true)
*/
private $route;
/**
* @var string
*
* @ORM\Column(type="string", length=16, nullable=true)
*/
private $method;
/**
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created;
/**
* @var integer
*
* @ORM\Column(name="status_code", type="integer", nullable=true)
*/
private $statusCode;
/**
* @var string
*
* @ORM\Column(type="string", length=64, nullable=true)
*/
private $format;
/**
* @var string
*
* @ORM\Column(type="string", length=64, nullable=true)
*/
private $resource;
/**
* @var string
*
* @ORM\Column(type="string", length=64, nullable=true)
*/
private $general;
/**
* @var string
*
* @ORM\Column(type="string", length=64, nullable=true)
*/
private $aspect;
/**
* @var string
*
* @ORM\Column(type="string", length=64, nullable=true)
*/
private $action;
/**
* @var integer
*
* @ORM\Column(name="error_code", type="integer", nullable=true)
*/
private $errorCode;
/**
* @var string
*
* @ORM\Column(name="error_message", type="text", nullable=true)
*/
private $errorMessage;
/**
* @param ApiAccount $account
*
* @return ApiLog
*/
public function setAccount(ApiAccount $account)
{
$this->account = $account;
return $this;
}
/**
* @return ApiAccount
*/
public function getAccount()
{
return $this->account;
}
/**
* @param string $action
*
* @return ApiLog
*/
public function setAction($action)
{
$this->action = $action;
return $this;
}
/**
* @return string
*/
public function getAction()
{
return $this->action;
}
/**
* @param string $aspect
*
* @return ApiLog
*/
public function setAspect($aspect)
{
$this->aspect = $aspect;
return $this;
}
/**
* @return string
*/
public function getAspect()
{
return $this->aspect;
}
/**
* @param integer $errorCode
*
* @return ApiLog
*/
public function setErrorCode($errorCode)
{
$this->errorCode = $errorCode;
return $this;
}
/**
* @return integer
*/
public function getErrorCode()
{
return $this->errorCode;
}
/**
* @param string $errorMessage
*
* @return ApiLog
*/
public function setErrorMessage($errorMessage)
{
$this->errorMessage = $errorMessage;
return $this;
}
/**
* @return string
*/
public function getErrorMessage()
{
return $this->errorMessage;
}
/**
* @param string $format
*
* @return ApiLog
*/
public function setFormat($format)
{
$this->format = $format;
return $this;
}
/**
* @return string
*/
public function getFormat()
{
return $this->format;
}
/**
* @param string $general
*
* @return ApiLog
*/
public function setGeneral($general)
{
$this->general = $general;
return $this;
}
/**
* @return string
*/
public function getGeneral()
{
return $this->general;
}
/**
* @param string $resource
*
* @return ApiLog
*/
public function setResource($resource)
{
$this->resource = $resource;
return $this;
}
/**
* @return string
*/
public function getResource()
{
return $this->resource;
}
/**
* @param string $route
*
* @return ApiLog
*/
public function setRoute($route)
{
$this->route = $route;
return $this;
}
/**
* @return string
*/
public function getRoute()
{
return $this->route;
}
/**
* @param integer $statusCode
*
* @return ApiLog
*/
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
/**
* @return integer
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* @param \DateTime $created
*
* @return ApiLog
*/
public function setCreated(\DateTime $created)
{
$this->created = $created;
return $this;
}
/**
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* @param string $method
*
* @return ApiLog
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* @return string
*/
public function getMethod()
{
return $this->method;
}
}

View File

@@ -0,0 +1,200 @@
<?php
namespace Alchemy\Phrasea\Model\Entities;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @ORM\Table(name="ApiOauthCodes", indexes={@ORM\Index(name="account_id", columns={"account_id"})})
* @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\ApiOauthCodeRepository")
*/
class ApiOauthCode
{
/**
* @var string
*
* @ORM\Column(name="code", type="string", length=128, nullable=false)
* @ORM\Id
*/
private $code;
/**
* @ORM\ManyToOne(targetEntity="ApiAccount")
* @ORM\JoinColumn(name="account_id", referencedColumnName="id", nullable=false)
*
* @return ApiAccount
**/
private $account;
/**
* @var string
*
* @ORM\Column(name="redirect_uri", type="string", length=128, nullable=false)
*/
private $redirectUri;
/**
* @ORM\Column(type="integer")
*/
private $expires;
/**
* @var string
*
* @ORM\Column(type="string", length=128, nullable=true)
*/
private $scope;
/**
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created;
/**
* @Gedmo\Timestampable(on="update")
* @ORM\Column(type="datetime")
*/
private $updated;
/**
* @param ApiAccount $account
*
* @return ApiOauthCode
*/
public function setAccount(ApiAccount $account)
{
$this->account = $account;
return $this;
}
/**
* @return ApiAccount
*/
public function getAccount()
{
return $this->account;
}
/**
* @param string $code
*
* @return ApiOauthCode
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* @return string
*/
public function getCode()
{
return $this->code;
}
/**
* @param \DateTime $created
*
* @return ApiOauthCode
*/
public function setCreated(\DateTime $created)
{
$this->created = $created;
return $this;
}
/**
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* @param integer $timestamp
*
* @return ApiOauthCode
*/
public function setExpires($timestamp)
{
$this->expires = $timestamp;
return $this;
}
/**
* @return $timestamp
*/
public function getExpires()
{
return $this->expires;
}
/**
* @param string $redirectUri
*
* @return ApiOauthCode
*/
public function setRedirectUri($redirectUri)
{
$this->redirectUri = $redirectUri;
return $this;
}
/**
* @return string
*/
public function getRedirectUri()
{
return $this->redirectUri;
}
/**
* @param string $scope
*
* @return ApiOauthCode
*/
public function setScope($scope)
{
$this->scope = $scope;
return $this;
}
/**
* @return string
*/
public function getScope()
{
return $this->scope;
}
/**
* @param \DateTime $updated
*
* @return ApiOauthCode
*/
public function setUpdated(\DateTime $updated)
{
$this->updated = $updated;
return $this;
}
/**
* @return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
}

View File

@@ -0,0 +1,173 @@
<?php
namespace Alchemy\Phrasea\Model\Entities;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @ORM\Table(name="ApiOauthRefreshTokens", indexes={@ORM\Index(name="account_id", columns={"account_id"})})
* @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\ApiOauthRefreshTokenRepository")
*/
class ApiOauthRefreshToken
{
/**
* @var string
*
* @ORM\Column(name="refresh_token", type="string", length=128, nullable=false)
* @ORM\Id
*/
private $refreshToken;
/**
* @ORM\ManyToOne(targetEntity="ApiAccount")
* @ORM\JoinColumn(name="account_id", referencedColumnName="id", nullable=false)
*
* @return ApiAccount
**/
private $account;
/**
* @ORM\Column(type="integer", nullable=false)
*/
private $expires;
/**
* @var string
*
* @ORM\Column(type="string", length=128, nullable=true)
*/
private $scope;
/**
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created;
/**
* @Gedmo\Timestampable(on="update")
* @ORM\Column(type="datetime")
*/
private $updated;
/**
* @param ApiAccount $account
*
* @return ApiOauthRefreshToken
*/
public function setAccount(ApiAccount $account)
{
$this->account = $account;
return $this;
}
/**
* @return ApiAccount
*/
public function getAccount()
{
return $this->account;
}
/**
* @param \DateTime $created
*
* @return ApiOauthRefreshToken
*/
public function setCreated(\DateTime $created)
{
$this->created = $created;
return $this;
}
/**
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* @param integer $expires
*
* @return ApiOauthRefreshToken
*/
public function setExpires($expires)
{
$this->expires = $expires;
return $this;
}
/**
* @return integer
*/
public function getExpires()
{
return $this->expires;
}
/**
* @param string $refreshToken
*
* @return ApiOauthRefreshToken
*/
public function setRefreshToken($refreshToken)
{
$this->refreshToken = $refreshToken;
return $this;
}
/**
* @return string
*/
public function getRefreshToken()
{
return $this->refreshToken;
}
/**
* @param string $scope
*
* @return ApiOauthRefreshToken
*/
public function setScope($scope)
{
$this->scope = $scope;
return $this;
}
/**
* @return string
*/
public function getScope()
{
return $this->scope;
}
/**
* @param \DateTime $updated
*
* @return ApiOauthRefreshToken
*/
public function setUpdated(\DateTime $updated)
{
$this->updated = $updated;
return $this;
}
/**
* @return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
}

View File

@@ -0,0 +1,229 @@
<?php
namespace Alchemy\Phrasea\Model\Entities;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @ORM\Table(name="ApiOauthTokens", indexes={
* @ORM\Index(name="account_id", columns={"account_id"}),
* @ORM\Index(name="session_id", columns={"session_id"})
* })
* @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\ApiOauthTokenRepository")
*/
class ApiOauthToken
{
/**
* @var string
*
* @ORM\Column(name="oauth_token", type="string", length=128, nullable=false)
* @ORM\Id
*/
private $oauthToken;
/**
* @ORM\Column(name="session_id", type="integer", nullable=true)
*/
private $sessionId;
/**
* @ORM\ManyToOne(targetEntity="ApiAccount", inversedBy="tokens")
* @ORM\JoinColumn(name="account_id", referencedColumnName="id", nullable=false)
*
* @return ApiAccount
**/
private $account;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $expires;
/**
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime", name="last_used")
*/
private $lastUsed;
/**
* @var string
*
* @ORM\Column(type="string", length=128, nullable=true)
*/
private $scope;
/**
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created;
/**
* @Gedmo\Timestampable(on="update")
* @ORM\Column(type="datetime")
*/
private $updated;
/**
* @param ApiAccount $account
*
* @return ApiOauthTokens
*/
public function setAccount(ApiAccount $account)
{
$account->addTokens($this);
$this->account = $account;
return $this;
}
/**
* @return ApiAccount
*/
public function getAccount()
{
return $this->account;
}
/**
* @param \DateTime $created
*
* @return ApiOauthTokens
*/
public function setCreated(\DateTime $created)
{
$this->created = $created;
return $this;
}
/**
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* @param integer $expires
*
* @return ApiOauthTokens
*/
public function setExpires($expires = null)
{
$this->expires = $expires;
return $this;
}
/**
* @return integer
*/
public function getExpires()
{
return $this->expires;
}
/**
* @param string $oauthToken
*
* @return ApiOauthTokens
*/
public function setOauthToken($oauthToken)
{
$this->oauthToken = $oauthToken;
return $this;
}
/**
* @return string
*/
public function getOauthToken()
{
return $this->oauthToken;
}
/**
* @param string $scope
*
* @return ApiOauthTokens
*/
public function setScope($scope)
{
$this->scope = $scope;
return $this;
}
/**
* @return string
*/
public function getScope()
{
return $this->scope;
}
/**
* @param integer $sessionId
*
* @return ApiOauthTokens
*/
public function setSessionId($sessionId)
{
$this->sessionId = $sessionId;
return $this;
}
/**
* @return Session
*/
public function getSessionId()
{
return $this->sessionId;
}
/**
* @param \DateTime $updated
*
* @return ApiOauthTokens
*/
public function setUpdated(\DateTime $updated)
{
$this->updated = $updated;
return $this;
}
/**
* @return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* @param \DateTime $lastUsed
*
* @return ApiOauthToken
*/
public function setLastUsed(\DateTime $lastUsed)
{
$this->lastUsed = $lastUsed;
return $this;
}
/**
* @return \DateTime
*/
public function getLastUsed()
{
return $this->lastUsed;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\ACLProvider;
use Alchemy\Phrasea\Controller\Api\V1;
use Alchemy\Phrasea\Model\Entities\ApiAccount;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Model\Entities\User;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
class ApiAccountManipulator implements ManipulatorInterface
{
private $om;
private $repository;
public function __construct(ObjectManager $om, EntityRepository $repo)
{
$this->om = $om;
$this->repository = $repo;
}
public function create(ApiApplication $application, User $user)
{
$account = new ApiAccount();
$account->setUser($user);
$account->setApplication($application);
$account->setApiVersion(V1::VERSION);
$this->update($account);
return $account;
}
public function delete(ApiAccount $account)
{
$this->om->remove($account);
$this->om->flush();
}
public function update(ApiAccount $account)
{
$this->om->persist($account);
$this->om->flush();
}
public function authorizeAccess(ApiAccount $account)
{
$account->setRevoked(false);
$this->update($account);
}
public function revokeAccess(ApiAccount $account)
{
$account->setRevoked(true);
$this->update($account);
}
}

View File

@@ -0,0 +1,117 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\ACLProvider;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
use RandomLib\Generator;
class ApiApplicationManipulator implements ManipulatorInterface
{
private $om;
private $repository;
private $randomGenerator;
public function __construct(ObjectManager $om, EntityRepository $repo, Generator $random)
{
$this->om = $om;
$this->repository = $repo;
$this->randomGenerator = $random;
}
public function create($name, $type, $description, $applicationWebsite, User $creator = null, $redirectUri = null)
{
$application = new ApiApplication();
$application->setCreator($creator);
$application->setName($name);
$application->setDescription($description);
$this->doSetType($application, $type);
$this->doSetWebsiteUrl($application, $applicationWebsite);
$this->doSetRedirectUri($application, $redirectUri);
$application->setNonce($this->randomGenerator->generateString(64));
$application->setClientId($this->randomGenerator->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS));
$application->setClientSecret($this->randomGenerator->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS));
$this->om->persist($application);
$this->om->flush();
return $application;
}
public function delete(ApiApplication $application)
{
$this->om->remove($application);
$this->om->flush();
}
public function update(ApiApplication $application)
{
$this->om->persist($application);
$this->om->flush();
}
public function setType(ApiApplication $application, $type)
{
$this->doSetType($application, $type);
$this->update($application);
}
public function setRedirectUri(ApiApplication $application, $uri)
{
$this->doSetRedirectUri($application, $uri);
$this->update($application);
}
public function setWebsiteUrl(ApiApplication $application, $url)
{
$this->doSetWebsiteUrl($application, $url);
$this->update($application);
}
private function doSetType(ApiApplication $application, $type)
{
if (!in_array($type, [ApiApplication::DESKTOP_TYPE, ApiApplication::WEB_TYPE])) {
throw new InvalidArgumentException(sprintf('%s api application type is not supported, it should be one of the following %s', $type, implode(', ', [ApiApplication::DESKTOP_TYPE, ApiApplication::WEB_TYPE])));
}
$application->setType($type);
}
private function doSetRedirectUri(ApiApplication $application, $uri)
{
if ($application->getType() === ApiApplication::DESKTOP_TYPE) {
$application->setRedirectUri(ApiApplication::NATIVE_APP_REDIRECT_URI);
return;
}
if (false === filter_var($uri, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) {
throw new InvalidArgumentException(sprintf('Redirect Uri Url %s is not legal.', $uri));
}
$application->setRedirectUri($uri);
}
private function doSetWebsiteUrl(ApiApplication $application, $url)
{
if (false === filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) {
throw new InvalidArgumentException(sprintf('Website Url %s is not legal.', $url));
}
$application->setWebsite($url);
}
}

View File

@@ -0,0 +1,215 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\ACLProvider;
use Alchemy\Phrasea\Model\Entities\ApiAccount;
use Alchemy\Phrasea\Model\Entities\ApiLog;
use Alchemy\Phrasea\Model\Entities\User;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ApiLogManipulator implements ManipulatorInterface
{
private $om;
private $repository;
public function __construct(ObjectManager $om, EntityRepository $repo)
{
$this->om = $om;
$this->repository = $repo;
}
public function create(ApiAccount $account, Request $request, Response $response)
{
$log = new ApiLog();
$log->setAccount($account);
$this->doSetFromHttpContext($log, $request, $response);
$this->update($log);
return $log;
}
public function delete(ApiLog $log)
{
$this->om->remove($log);
$this->om->flush();
}
public function update(ApiLog $log)
{
$this->om->persist($log);
$this->om->flush();
}
private function doSetFromHttpContext(ApiLog $log, Request $request, Response $response)
{
$log->setRoute($request->getPathInfo());
$log->setMethod($request->getMethod());
$log->setStatusCode($response->getStatusCode());
$log->setFormat($response->headers->get('content-type'));
$this->setDetails($log, $request, $response);
}
/**
* Parses the requested route to fetch
* - the resource (databox, basket, record etc ..)
* - general action (list, add, search)
* - the action (setstatus, setname etc..)
* - the aspect (collections, related, content etc..)
*
* @param ApiLog $log
* @param Request $request
* @param Response $response
*/
private function setDetails(ApiLog $log, Request $request, Response $response)
{
$chunks = explode('/', trim($request->getPathInfo(), '/'));
if (false === $response->isOk() || sizeof($chunks) === 0) {
return;
}
switch ($chunks[0]) {
case ApiLog::DATABOXES_RESOURCE :
$this->hydrateDataboxes($log, $chunks);
break;
case ApiLog::RECORDS_RESOURCE :
$this->hydrateRecords($log, $chunks);
break;
case ApiLog::BASKETS_RESOURCE :
$this->hydrateBaskets($log, $chunks);
break;
case ApiLog::FEEDS_RESOURCE :
$this->hydrateFeeds($log, $chunks);
break;
case ApiLog::QUARANTINE_RESOURCE :
$this->hydrateQuarantine($log, $chunks);
break;
case ApiLog::STORIES_RESOURCE :
$this->hydrateStories($log, $chunks);
break;
case ApiLog::MONITOR_RESOURCE :
$this->hydrateMonitor($log, $chunks);
break;
}
}
private function hydrateDataboxes(ApiLog $log, $chunks)
{
$log->setResource($chunks[0]);
$log->setGeneral($chunks[0]);
if (count($chunks) === 2) {
$log->setAction($chunks[1]);
}
if ((int) $chunks[1] > 0 && count($chunks) === 3) {
$log->setAspect($chunks[2]);
}
}
private function hydrateRecords(ApiLog $log, $chunks)
{
$log->setResource($chunks[0]);
$log->setGeneral($chunks[0]);
if (count($chunks) === 2) {
$log->setAction($chunks[1]);
}
if (count($chunks) === 3 && (int) $chunks[1] > 0 && (int) $chunks[2] > 0) {
$log->setAction('get');
}
if ((int) $chunks[1] > 0 && (int) $chunks[2] > 0 && count($chunks) == 4) {
if (preg_match("/^set/", $chunks[3])) {
$log->setAction($chunks[3]);
} else {
$log->setAspect($chunks[3]);
}
}
}
private function hydrateBaskets(ApiLog $log, $chunks)
{
$log->setResource($chunks[0]);
$log->setGeneral($chunks[0]);
if (count($chunks) === 2) {
$log->setAction($chunks[1]);
}
if ((int) $chunks[1] > 0 && count($chunks) == 3) {
if (preg_match("/^set/", $chunks[2]) || preg_match("/^delete/", $chunks[2])) {
$log->setAction($chunks[2]);
} else {
$log->setAspect($chunks[2]);
}
}
}
private function hydrateFeeds(ApiLog $log, $chunks)
{
$log->setResource($chunks[0]);
$log->setGeneral($chunks[0]);
if (count($chunks) === 2) {
if (preg_match("/^content$/", $chunks[1])) {
$log->setAspect($chunks[1]);
} else {
$log->setAction($chunks[1]);
}
}
if (count($chunks) === 3) {
if ((int) $chunks[1] > 0) {
$log->setAspect($chunks[2]);
}
if (preg_match("/^entry$/", $chunks[1]) && (int) $chunks[2] > 0) {
$log->setAspect($chunks[1]);
}
}
}
private function hydrateQuarantine(ApiLog $log, $chunks)
{
$log->setResource($chunks[0]);
$log->setGeneral($chunks[0]);
if (count($chunks) === 2) {
$log->setAction($chunks[1]);
}
}
private function hydrateStories(ApiLog $log, $chunks)
{
$log->setGeneral($chunks[0]);
$log->setResource($chunks[0]);
if ((int) $chunks[1] > 0 && (int) $chunks[2] > 0 && count($chunks) == 4) {
$log->setAspect($chunks[3]);
}
if (count($chunks) === 3 && (int) $chunks[1] > 0 && (int) $chunks[2] > 0) {
$log->setAction('get');
}
}
private function hydrateMonitor(ApiLog $log, $chunks)
{
$log->setGeneral($chunks[0]);
if (count($chunks) === 2) {
$log->setAspect($chunks[1]);
}
if (count($chunks) === 3 && (int) $chunks[2] > 0) {
$log->setAspect($chunks[1]);
$log->setAction('get');
}
if (count($chunks) === 4) {
$log->setAspect($chunks[1]);
$log->setAction($chunks[3]);
}
}
}

View File

@@ -0,0 +1,97 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\ACLProvider;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Entities\ApiAccount;
use Alchemy\Phrasea\Model\Entities\ApiOauthCode;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
use RandomLib\Generator;
class ApiOauthCodeManipulator implements ManipulatorInterface
{
private $om;
private $repository;
private $randomGenerator;
public function __construct(ObjectManager $om, EntityRepository $repo, Generator $random)
{
$this->om = $om;
$this->repository = $repo;
$this->randomGenerator = $random;
}
public function create(ApiAccount $account, $redirectUri, $expire, $scope = null)
{
$code = new ApiOauthCode();
$code->setCode($this->getNewCode());
$this->doSetRedirectUri($code, $redirectUri);
$code->setAccount($account);
$code->setExpires($expire);
$code->setScope($scope);
$this->update($code);
return $code;
}
public function delete(ApiOauthCode $code)
{
$this->om->remove($code);
$this->om->flush();
}
public function update(ApiOauthCode $code)
{
$this->om->persist($code);
$this->om->flush();
}
public function setCode(ApiOauthCode $code, $oauthCode)
{
$code->setCode($oauthCode);
$this->update($code);
}
public function setRedirectUri(ApiOauthCode $code, $uri)
{
$this->doSetRedirectUri($code, $uri);
$this->update($code);
}
private function doSetRedirectUri(ApiOauthCode $code, $uri)
{
if (false === filter_var($uri, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)
&& $uri !== ApiApplication::NATIVE_APP_REDIRECT_URI
) {
throw new InvalidArgumentException(sprintf('Redirect Uri Url %s is not legal.', $uri));
}
$code->setRedirectUri($uri);
}
private function getNewCode()
{
do {
$code = $this->randomGenerator->generateString(16, TokenManipulator::LETTERS_AND_NUMBERS);
} while (null !== $this->repository->find($code));
return $code;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\ACLProvider;
use Alchemy\Phrasea\Model\Entities\ApiAccount;
use Alchemy\Phrasea\Model\Entities\ApiOauthRefreshtoken;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
use RandomLib\Generator;
class ApiOauthRefreshTokenManipulator implements ManipulatorInterface
{
private $om;
private $repository;
private $randomGenerator;
public function __construct(ObjectManager $om, EntityRepository $repo, Generator $random)
{
$this->om = $om;
$this->repository = $repo;
$this->randomGenerator = $random;
}
public function create(ApiAccount $account, $expire, $scope = null)
{
$refreshToken = new ApiOauthRefreshtoken();
$refreshToken->setCode($this->getNewToken());
$refreshToken->setAccount($account);
$refreshToken->setExpires($expire);
$refreshToken->setScope($scope);
$this->update($refreshToken);
return $refreshToken;
}
public function delete(ApiOauthRefreshtoken $refreshToken)
{
$this->om->remove($refreshToken);
$this->om->flush();
}
public function update(ApiOauthRefreshtoken $refreshToken)
{
$this->om->persist($refreshToken);
$this->om->flush();
}
public function setRefreshToken(ApiOauthRefreshtoken $refreshToken, $token)
{
$refreshToken->setRefreshToken($token);
$this->update($refreshToken);
}
private function getNewToken()
{
do {
$refreshToken = $this->randomGenerator->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS);
} while (null !== $this->repository->find($refreshToken));
return $refreshToken;
}
}

View File

@@ -0,0 +1,98 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\ACLProvider;
use Alchemy\Phrasea\Model\Entities\ApiAccount;
use Alchemy\Phrasea\Model\Entities\ApiOauthToken;
use Alchemy\Phrasea\Model\Entities\Session;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
use RandomLib\Generator;
class ApiOauthTokenManipulator implements ManipulatorInterface
{
private $om;
private $repository;
private $randomGenerator;
public function __construct(ObjectManager $om, EntityRepository $repo, Generator $random)
{
$this->om = $om;
$this->repository = $repo;
$this->randomGenerator = $random;
}
public function create(ApiAccount $account, $expire = null, $scope = null)
{
$token = new ApiOauthToken();
$token->setOauthToken($this->getNewToken());
$token->setExpires($expire);
$token->setScope($scope);
$token->setAccount($account);
$this->om->persist($account);
$this->update($token);
return $token;
}
public function delete(ApiOauthToken $token)
{
$this->om->remove($token);
$this->om->flush();
}
public function update(ApiOauthToken $token)
{
$this->om->persist($token);
$this->om->flush();
}
public function setLastUsed(ApiOauthToken $token, \DateTime $date)
{
$token->setLastUsed($date);
$this->update($token);
}
public function rememberSessionId(ApiOauthToken $token, $sessionId)
{
$token->setSessionId($sessionId);
$this->update($token);
}
public function renew(ApiOauthToken $token, $expire = null)
{
$token->setOauthToken($this->getNewToken());
$token->setExpires($expire);
$this->update($token);
}
public function setOauthToken(ApiOauthToken $token, $oauthToken)
{
$token->setOauthToken($oauthToken);
$this->update($token);
}
private function getNewToken()
{
do {
$token = $this->randomGenerator->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS);
} while (null !== $this->repository->find($token));
return $token;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Alchemy\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Model\Entities\User;
use Doctrine\ORM\EntityRepository;
/**
* ApiAccountRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ApiAccountRepository extends EntityRepository
{
public function findByUserAndApplication(User $user, ApiApplication $application)
{
$qb = $this->createQueryBuilder('acc');
$qb->where($qb->expr()->eq('acc.user', ':user'));
$qb->andWhere($qb->expr()->eq('acc.application', ':app'));
$qb->setParameter(':user', $user);
$qb->setParameter(':app', $application);
return $qb->getQuery()->getOneOrNullResult();
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Alchemy\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\User;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr;
/**
* ApiApplicationRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ApiApplicationRepository extends EntityRepository
{
public function findByClientId($clientId)
{
$qb = $this->createQueryBuilder('app');
$qb->where($qb->expr()->eq('app.clientId', ':clientId'));
$qb->setParameter(':clientId', $clientId);
return $qb->getQuery()->getOneOrNullResult();
}
public function findByCreator(User $user)
{
$qb = $this->createQueryBuilder('app');
$qb->where($qb->expr()->eq('app.creator', ':creator'));
$qb->setParameter(':creator', $user);
return $qb->getQuery()->getResult();
}
public function findByUser(User $user)
{
$qb = $this->createQueryBuilder('app');
$qb->innerJoin('app.accounts', 'acc', Expr\Join::WITH, $qb->expr()->eq('acc.user', ':user'));
$qb->setParameter(':user', $user);
return $qb->getQuery()->getResult();
}
public function findAuthorizedAppsByUser(User $user)
{
$qb = $this->createQueryBuilder('app');
$qb->innerJoin('app.accounts', 'acc', Expr\Join::WITH, $qb->expr()->eq('acc.user', ':user'));
$qb->where($qb->expr()->eq('acc.revoked', $qb->expr()->literal(false)));
$qb->setParameter(':user', $user);
return $qb->getQuery()->getResult();
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Alchemy\Phrasea\Model\Repositories;
use Doctrine\ORM\EntityRepository;
/**
* ApiLogRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ApiLogRepository extends EntityRepository
{
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Alchemy\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\ApiAccount;
use Doctrine\ORM\EntityRepository;
/**
* ApiOauthCodeRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ApiOauthCodeRepository extends EntityRepository
{
public function findByAccount(ApiAccount $account)
{
$qb = $this->createQueryBuilder('c');
$qb->where($qb->expr()->eq('c.account', ':account'));
$qb->setParameter(':account', $account);
return $qb->getQuery()->getResult();
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Alchemy\Phrasea\Model\Repositories;
use Doctrine\ORM\EntityRepository;
/**
* ApiOauthRefreshTokenRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ApiOauthRefreshTokenRepository extends EntityRepository
{
public function findByAccount(ApiAccount $account)
{
$qb = $this->createQueryBuilder('rt');
$qb->where($qb->expr()->eq('rt.account', ':account'));
$qb->setParameter(':account', $account);
return $qb->getQuery()->getResult();
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace Alchemy\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\ApiAccount;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr;
/**
* ApiOauthTokenRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ApiOauthTokenRepository extends EntityRepository
{
public function findDeveloperToken(ApiAccount $account)
{
$qb = $this->createQueryBuilder('tok');
$qb->innerJoin('tok.account', 'acc', Expr\Join::WITH, $qb->expr()->eq('acc.id', ':acc_id'));
$qb->innerJoin('acc.application', 'app', Expr\Join::WITH, $qb->expr()->orx(
$qb->expr()->eq('app.creator', 'acc.user'),
$qb->expr()->isNull('app.creator')
));
$qb->where($qb->expr()->isNull('tok.expires'));
$qb->orderBy('tok.created', 'DESC');
$qb->setParameter(':acc_id', $account->getId());
/*
* @note until we add expiration token, there is no way to distinguish a developer issued token from
* a connection process issued token.
*/
$tokens = $qb->getQuery()->getResult();
if (0 === count($tokens)) {
return null;
}
return current($tokens);
}
public function findOauthTokens(ApiAccount $account)
{
$qb = $this->createQueryBuilder('tok');
$qb->where($qb->expr()->eq('tok.account', ':acc'));
$qb->setParameter(':acc', $account);
return $qb->getQuery()->getResult();
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Setup\DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
class ApiMigration extends AbstractMigration
{
public function isAlreadyApplied()
{
return $this->tableExists('ApiApplication');
}
public function doUpSql(Schema $schema)
{
$this->addSql("CREATE TABLE ApiLogs (id INT AUTO_INCREMENT NOT NULL, account_id INT NOT NULL, route VARCHAR(128) DEFAULT NULL, method VARCHAR(16) DEFAULT NULL, created DATETIME NOT NULL, status_code INT DEFAULT NULL, format VARCHAR(64) DEFAULT NULL, resource VARCHAR(64) DEFAULT NULL, general VARCHAR(64) DEFAULT NULL, aspect VARCHAR(64) DEFAULT NULL, action VARCHAR(64) DEFAULT NULL, error_code INT DEFAULT NULL, error_message LONGTEXT DEFAULT NULL, INDEX IDX_91E90F309B6B5FBA (account_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
$this->addSql("CREATE TABLE ApiApplications (id INT AUTO_INCREMENT NOT NULL, creator_id INT DEFAULT NULL, type VARCHAR(128) NOT NULL, name VARCHAR(128) NOT NULL, description LONGTEXT NOT NULL, website VARCHAR(128) NOT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL, client_id VARCHAR(32) NOT NULL, client_secret VARCHAR(32) NOT NULL, nonce VARCHAR(64) NOT NULL, redirect_uri VARCHAR(128) NOT NULL, activated TINYINT(1) NOT NULL, grant_password TINYINT(1) NOT NULL, INDEX IDX_53F7BBE661220EA6 (creator_id), UNIQUE INDEX client_id (client_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
$this->addSql("CREATE TABLE ApiOauthCodes (code VARCHAR(128) NOT NULL, account_id INT NOT NULL, redirect_uri VARCHAR(128) NOT NULL, expires DATETIME DEFAULT NULL, scope VARCHAR(128) DEFAULT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL, INDEX IDX_BE6B11809B6B5FBA (account_id), PRIMARY KEY(code)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
$this->addSql("CREATE TABLE ApiOauthRefreshTokens (refresh_token VARCHAR(128) NOT NULL, account_id INT NOT NULL, expires DATETIME NOT NULL, scope VARCHAR(128) DEFAULT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL, INDEX IDX_7DA42A5A9B6B5FBA (account_id), PRIMARY KEY(refresh_token)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
$this->addSql("CREATE TABLE ApiOauthTokens (oauth_token VARCHAR(128) NOT NULL, account_id INT NOT NULL, session_id INT DEFAULT NULL, expires DATETIME DEFAULT NULL, last_used DATETIME NOT NULL, scope VARCHAR(128) DEFAULT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL, INDEX IDX_4FD469539B6B5FBA (account_id), INDEX session_id (session_id), PRIMARY KEY(oauth_token)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
$this->addSql("CREATE TABLE ApiAccounts (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, application_id INT NOT NULL, revoked TINYINT(1) NOT NULL, api_version VARCHAR(16) NOT NULL, created DATETIME NOT NULL, INDEX IDX_2C54E637A76ED395 (user_id), INDEX IDX_2C54E6373E030ACD (application_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
$this->addSql("ALTER TABLE ApiLogs ADD CONSTRAINT FK_91E90F309B6B5FBA FOREIGN KEY (account_id) REFERENCES ApiAccounts (id)");
$this->addSql("ALTER TABLE ApiApplications ADD CONSTRAINT FK_53F7BBE661220EA6 FOREIGN KEY (creator_id) REFERENCES Users (id)");
$this->addSql("ALTER TABLE ApiOauthCodes ADD CONSTRAINT FK_BE6B11809B6B5FBA FOREIGN KEY (account_id) REFERENCES ApiAccounts (id)");
$this->addSql("ALTER TABLE ApiOauthRefreshTokens ADD CONSTRAINT FK_7DA42A5A9B6B5FBA FOREIGN KEY (account_id) REFERENCES ApiAccounts (id)");
$this->addSql("ALTER TABLE ApiOauthTokens ADD CONSTRAINT FK_4FD469539B6B5FBA FOREIGN KEY (account_id) REFERENCES ApiAccounts (id)");
$this->addSql("ALTER TABLE ApiAccounts ADD CONSTRAINT FK_2C54E637A76ED395 FOREIGN KEY (user_id) REFERENCES Users (id)");
$this->addSql("ALTER TABLE ApiAccounts ADD CONSTRAINT FK_2C54E6373E030ACD FOREIGN KEY (application_id) REFERENCES ApiApplications (id)");
}
public function doDownSql(Schema $schema)
{
$this->addSql("ALTER TABLE ApiAccounts DROP FOREIGN KEY FK_2C54E6373E030ACD");
$this->addSql("ALTER TABLE ApiLogs DROP FOREIGN KEY FK_91E90F309B6B5FBA");
$this->addSql("ALTER TABLE ApiOauthCodes DROP FOREIGN KEY FK_BE6B11809B6B5FBA");
$this->addSql("ALTER TABLE ApiOauthRefreshTokens DROP FOREIGN KEY FK_7DA42A5A9B6B5FBA");
$this->addSql("ALTER TABLE ApiOauthTokens DROP FOREIGN KEY FK_4FD469539B6B5FBA");
$this->addSql("DROP TABLE ApiLogs");
$this->addSql("DROP TABLE ApiApplications");
$this->addSql("DROP TABLE ApiOauthCodes");
$this->addSql("DROP TABLE ApiOauthRefreshTokens");
$this->addSql("DROP TABLE ApiOauthTokens");
$this->addSql("DROP TABLE ApiAccounts");
}
}

View File

@@ -12,10 +12,12 @@
namespace Alchemy\Phrasea\Setup; namespace Alchemy\Phrasea\Setup;
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Core\Event\InstallFinishEvent;
use Alchemy\Phrasea\Model\Entities\User;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\DBALException; use Doctrine\DBAL\DBALException;
use Doctrine\ORM\Tools\SchemaTool; use Doctrine\ORM\Tools\SchemaTool;
use Alchemy\Phrasea\Model\Entities\User;
class Installer class Installer
{ {
@@ -46,6 +48,8 @@ class Installer
throw $e; throw $e;
} }
$this->app['dispatcher']->dispatch(PhraseaEvents::INSTALL_FINISH, new InstallFinishEvent($user));
return $user; return $user;
} }

View File

@@ -1,263 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Alchemy\Phrasea\Model\Entities\User;
class API_OAuth2_Account
{
/**
*
* @var Application
*/
protected $app;
/**
*
* @var int
*/
protected $id;
/**
*
* @var User
*/
protected $user;
/**
*
* @var API_OAuth2_Application
*/
protected $application;
/**
*
* @var int
*/
protected $application_id;
/**
*
* @var string
*/
protected $api_version;
/**
*
* @var boolean
*/
protected $revoked;
/**
*
* @var DateTime
*/
protected $created_on;
/**
*
* @var string
*/
protected $token;
public function __construct(Application $app, $account_id)
{
$this->app = $app;
$this->id = (int) $account_id;
$sql = 'SELECT api_account_id, usr_id, api_version, revoked
, application_id, created
FROM api_accounts
WHERE api_account_id = :api_account_id';
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':api_account_id' => $this->id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$this->application_id = (int) $row['application_id'];
$this->user = $app['repo.users']->find($row['usr_id']);
$this->api_version = $row['api_version'];
$this->revoked = ! ! $row['revoked'];
$this->created_on = new DateTime($row['created']);
return $this;
}
/**
*
* @return int
*/
public function get_id()
{
return $this->id;
}
/**
*
* @return User
*/
public function get_user()
{
return $this->user;
}
/**
*
* @return string
*/
public function get_api_version()
{
return $this->api_version;
}
/**
*
* @return boolean
*/
public function is_revoked()
{
return $this->revoked;
}
/**
*
* @param boolean $boolean
* @return API_OAuth2_Account
*/
public function set_revoked($boolean)
{
$this->revoked = ! ! $boolean;
$sql = 'UPDATE api_accounts SET revoked = :revoked
WHERE api_account_id = :account_id';
$params = [
':revoked' => ($boolean ? '1' : '0')
, 'account_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
*
* @return DateTime
*/
public function get_created_on()
{
return $this->created_on;
}
/**
*
* @return API_OAuth2_Token
*/
public function get_token()
{
if (! $this->token) {
try {
$this->token = new API_OAuth2_Token($this->app['phraseanet.appbox'], $this, $this->app['random.medium']);
} catch (NotFoundHttpException $e) {
$this->token = API_OAuth2_Token::create($this->app['phraseanet.appbox'], $this, $this->app['random.medium']);
}
}
return $this->token;
}
/**
*
* @return API_OAuth2_Application
*/
public function get_application()
{
if ( ! $this->application)
$this->application = new API_OAuth2_Application($this->app, $this->application_id);
return $this->application;
}
/**
*
* @return void
*/
public function delete()
{
$this->get_token()->delete();
foreach (API_OAuth2_AuthCode::load_codes_by_account($this->app, $this) as $code) {
$code->delete();
}
foreach (API_OAuth2_RefreshToken::load_by_account($this->app, $this) as $token) {
$token->delete();
}
$sql = 'DELETE FROM api_accounts WHERE api_account_id = :account_id';
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute(['account_id' => $this->id]);
$stmt->closeCursor();
return;
}
public static function create(Application $app, User $user, API_OAuth2_Application $application)
{
$sql = 'INSERT INTO api_accounts
(api_account_id, usr_id, revoked, api_version, application_id, created)
VALUES (null, :usr_id, :revoked, :api_version, :application_id, :created)';
$datetime = new Datetime();
$params = [
':usr_id' => $user->getId()
, ':application_id' => $application->get_id()
, ':api_version' => API_OAuth2_Adapter::API_VERSION
, ':revoked' => 0
, ':created' => $datetime->format("Y-m-d H:i:s")
];
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$account_id = $app['phraseanet.appbox']->get_connection()->lastInsertId();
return new self($app, $account_id);
}
public static function load_with_user(Application $app, API_OAuth2_Application $application, User $user)
{
$sql = 'SELECT api_account_id FROM api_accounts
WHERE usr_id = :usr_id AND application_id = :application_id';
$params = [
":usr_id" => $user->getId(),
":application_id" => $application->get_id()
];
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if (! $row) {
throw new NotFoundHttpException('Account nof found.');
}
return new self($app, $row['api_account_id']);
}
}

View File

@@ -13,7 +13,12 @@ use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\Exception\AccountLockedException; use Alchemy\Phrasea\Authentication\Exception\AccountLockedException;
use Alchemy\Phrasea\Authentication\Exception\RequireCaptchaException; use Alchemy\Phrasea\Authentication\Exception\RequireCaptchaException;
use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Model\Entities\User;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class API_OAuth2_Adapter extends OAuth2 class API_OAuth2_Adapter extends OAuth2
{ {
@@ -24,7 +29,7 @@ class API_OAuth2_Adapter extends OAuth2
/** /**
* *
* @var API_OAuth2_Application * @var ApiApplication
*/ */
protected $client; protected $client;
@@ -64,12 +69,6 @@ class API_OAuth2_Adapter extends OAuth2
*/ */
protected $session_id; protected $session_id;
/**
*
* @var string
*/
protected $usr_id_requested;
/** /**
* access token of current request * access token of current request
* @var string * @var string
@@ -81,13 +80,11 @@ class API_OAuth2_Adapter extends OAuth2
* @param Application $app * @param Application $app
* @return API_OAuth2_Adapter * @return API_OAuth2_Adapter
*/ */
public function __construct(Application $app) public function __construct(Application $app, array $conf = [])
{ {
parent::__construct(); parent::__construct($conf);
$this->params = [];
$this->app = $app; $this->app = $app;
$this->params = [];
return $this;
} }
/** /**
@@ -101,7 +98,7 @@ class API_OAuth2_Adapter extends OAuth2
/** /**
* *
* @return API_OAuth2_Application * @return ApiApplication
*/ */
public function getClient() public function getClient()
{ {
@@ -125,11 +122,7 @@ class API_OAuth2_Adapter extends OAuth2
return $this->token; return $this->token;
} }
/** public function setClient(ApiApplication $client)
*
* @param API_OAuth2_Application $client
*/
public function setClient(API_OAuth2_Application $client)
{ {
$this->client = $client; $this->client = $client;
@@ -155,100 +148,84 @@ class API_OAuth2_Adapter extends OAuth2
} }
/** /**
*
* @return int
*/
public function get_usr_id()
{
return $this->usr_id;
}
/**
*
* Implements OAuth2::checkClientCredentials(). * Implements OAuth2::checkClientCredentials().
* *
* @param string $client_id * @param string $clientId
* @param string $client_secret * @param string $clientSecret
* @return boolean * @return boolean
*/ */
protected function checkClientCredentials($client_id, $client_secret = NULL) protected function checkClientCredentials($clientId, $clientSecret = null)
{ {
try { if (null === $application = $this->app['repo.api-applications']->findByClientId($clientId)) {
$application = API_OAuth2_Application::load_from_client_id($this->app, $client_id); return false;
if ($client_secret === NULL) {
return true;
}
return ($application->get_client_secret() === $client_secret);
} catch (\Exception $e) {
} }
return false; if (null === $clientSecret) {
return true;
}
return $application->getClientSecret() === $clientSecret;
} }
/** /**
* *
* Implements OAuth2::getRedirectUri(). * Implements OAuth2::getRedirectUri().
* *
* @param string $client_id * @param $clientId
* @return string *
* @return mixed
* @throws RuntimeException
*/ */
protected function getRedirectUri($client_id) protected function getRedirectUri($clientId)
{ {
$application = API_OAuth2_Application::load_from_client_id($this->app, $client_id); if (null === $application = $this->app['repo.api-applications']->findByClientId($clientId)) {
throw new BadRequestHttpException(sprintf('Application with client id %s could not be found', $clientId));
}
return $application->get_redirect_uri(); return $application->getRedirectUri();
} }
/** /**
* *
* Implements OAuth2::getAccessToken(). * Implements OAuth2::getAccessToken().
* *
* @param string $oauth_token * @param string $oauthToken
* @return array * @return array
*/ */
protected function getAccessToken($oauth_token) protected function getAccessToken($oauthToken)
{ {
$result = null; if (null === $token = $this->app['repo.api-oauth-tokens']->find($oauthToken)) {
return null;
try {
$token = API_OAuth2_Token::load_by_oauth_token($this->app, $oauth_token);
$result = [
'scope' => $token->get_scope()
, 'expires' => $token->get_expires()
, 'client_id' => $token->get_account()->get_application()->get_client_id()
, 'session_id' => $token->get_session_id()
, 'revoked' => ($token->get_account()->is_revoked() ? '1' : '0')
, 'usr_id' => $token->get_account()->get_user()->getId()
, 'oauth_token' => $token->get_value()
];
} catch (\Exception $e) {
} }
return $result; return [
'scope' => $token->getScope(),
'expires' => $token->getExpires(),
'client_id' => $token->getAccount()->getApplication()->getClientId(),
'session_id' => $token->getSessionId(),
'revoked' => (int) $token->getAccount()->isRevoked(),
'usr_id' => $token->getAccount()->getUser()->getId(),
'oauth_token' => $token->getOauthToken(),
];
} }
/** /**
* Implements OAuth2::setAccessToken(). * Implements OAuth2::setAccessToken().
*/
/**
* *
* @param type $oauth_token * @param $oauthToken
* @param type $account_id * @param $accountId
* @param type $expires * @param $expires
* @param string $scope * @param null $scope
* @return API_OAuth2_Adapter *
* @return $this
* @throws RuntimeException
*/ */
protected function setAccessToken($oauth_token, $account_id, $expires, $scope = NULL) protected function setAccessToken($oauthToken, $accountId, $expires = null, $scope = null)
{ {
$account = new API_OAuth2_Account($this->app, $account_id); if (null === $account = $this->app['repo.api-accounts']->find($accountId)) {
$token = API_OAuth2_Token::create($this->app['phraseanet.appbox'], $account, $this->app['random.medium'], $scope); throw new RuntimeException(sprintf('Account with id %s is not valid', $accountId));
$token->set_value($oauth_token)->set_expires($expires); }
$token = $this->app['manipulator.api-oauth-token']->create($account, $expires, $scope);
$this->app['manipulator.api-oauth-token']->setOauthToken($token, $oauthToken);
return $this; return $this;
} }
@@ -279,87 +256,104 @@ class API_OAuth2_Adapter extends OAuth2
} }
/** /**
*
* Overrides OAuth2::getAuthCode(). * Overrides OAuth2::getAuthCode().
* *
* @return array * @param $code
*
* @return array|null
*/ */
protected function getAuthCode($code) protected function getAuthCode($code)
{ {
try { if (null === $code = $this->app['repo.api-oauth-codes']->find($code)) {
$code = new API_OAuth2_AuthCode($this->app, $code); return null;
return [
'redirect_uri' => $code->get_redirect_uri()
, 'client_id' => $code->get_account()->get_application()->get_client_id()
, 'expires' => $code->get_expires()
, 'account_id' => $code->get_account()->get_id()
];
} catch (\Exception $e) {
} }
return null; return [
'redirect_uri' => $code->getRedirectUri(),
'client_id' => $code->getAccount()->getApplication()->getClientId(),
'expires' => $code->getExpires(),
'account_id' => $code->getAccount()->getId(),
];
} }
/** /**
* *
* Overrides OAuth2::setAuthCode(). * Overrides OAuth2::setAuthCode().
*
* @param $oauthCode
* @param $accountId
* @param $redirectUri
* @param $expires
* @param null $scope
* *
* @param string $code * @return $this|void
* @param int $account_id * @throws RuntimeException
* @param string $redirect_uri
* @param string $expires
* @param string $scope
* @return API_OAuth2_Adapter
*/ */
protected function setAuthCode($code, $account_id, $redirect_uri, $expires, $scope = NULL) protected function setAuthCode($oauthCode, $accountId, $redirectUri, $expires = null, $scope = null)
{ {
$account = new API_OAuth2_Account($this->app, $account_id); if (null === $account = $this->app['repo.api-accounts']->find($accountId)) {
$code = API_OAuth2_AuthCode::create($this->app, $account, $code, $expires); throw new RuntimeException(sprintf('Account with id %s is not valid', $accountId));
$code->set_redirect_uri($redirect_uri)->set_scope($scope); }
$code = $this->app['manipulator.api-oauth-code']->create($account, $redirectUri, $expires, $scope);
$this->app['manipulator.api-oauth-code']->setCode($code, $oauthCode);
return $this; return $this;
} }
/** /**
* Overrides OAuth2::setRefreshToken(). * Overrides OAuth2::setRefreshToken().
*
* @param $refreshToken
* @param $accountId
* @param $expires
* @param null $scope
*
* @return $this|void
* @throws RuntimeException
*/ */
protected function setRefreshToken($refresh_token, $account_id, $expires, $scope = NULL) protected function setRefreshToken($refreshToken, $accountId, $expires, $scope = null)
{ {
$account = new API_OAuth2_Account($this->app, $account_id); if (null === $account = $this->app['repo.api-accounts']->find($accountId)) {
API_OAuth2_RefreshToken::create($this->app, $account, $expires, $refresh_token, $scope); throw new RuntimeException(sprintf('Account with id %s is not valid', $accountId));
}
$token = $this->app['manipulator.api-oauth-refresh-token']->create($account, $expires, $scope);
$this->app['manipulator.api-oauth-refresh-token']->setRefreshToken($token, $refreshToken);
return $this; return $this;
} }
/** /**
* Overrides OAuth2::getRefreshToken(). * Overrides OAuth2::getRefreshToken().
*
* @param $refreshToken
*
* @return array|null
*/ */
protected function getRefreshToken($refresh_token) protected function getRefreshToken($refreshToken)
{ {
try { if (null === $token = $this->app['repo.api-oauth-refresh-token']->find($refreshToken)) {
$token = new API_OAuth2_RefreshToken($this->app, $refresh_token); return null;
return [
'token' => $token->get_value()
, 'expires' => $token->get_expires()->format('U')
, 'client_id' => $token->get_account()->get_application()->get_client_id()
];
} catch (\Exception $e) {
} }
return null; return [
'token' => $token->getRefreshToken(),
'expires' => $token->getExpires(),
'client_id' => $token->getAccount()->getApplication()->getClientId()
];
} }
/** /**
* Overrides OAuth2::unsetRefreshToken(). * Overrides OAuth2::unsetRefreshToken().
*
* @param $refreshToken
*
* @return $this|void
*/ */
protected function unsetRefreshToken($refresh_token) protected function unsetRefreshToken($refreshToken)
{ {
$token = new API_OAuth2_RefreshToken($this->app, $refresh_token); if (null !== $token = $this->app['repo.api-oauth-refresh-token']->find($refreshToken)) {
$token->delete(); $this->app['manipulator.api-oauth-refresh-token']->delete($token);
}
return $this; return $this;
} }
@@ -371,22 +365,21 @@ class API_OAuth2_Adapter extends OAuth2
*/ */
public function getAuthorizationRequestParameters(Request $request) public function getAuthorizationRequestParameters(Request $request)
{ {
$data = [
$datas = [ 'response_type' => $request->get('response_type', false),
'response_type' => $request->get('response_type', false) 'client_id' => $request->get('client_id', false),
, 'client_id' => $request->get('client_id', false) 'redirect_uri' => $request->get('redirect_uri', false),
, 'redirect_uri' => $request->get('redirect_uri', false)
]; ];
$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; $data["state"] = $state;
} }
if ($scope) { if ($scope) {
$datas["scope"] = $scope; $data["scope"] = $scope;
} }
$filters = [ $filters = [
@@ -405,17 +398,16 @@ class API_OAuth2_Adapter extends OAuth2
, "scope" => ["flags" => FILTER_REQUIRE_SCALAR] , "scope" => ["flags" => FILTER_REQUIRE_SCALAR]
]; ];
$input = filter_var_array($datas, $filters); $input = filter_var_array($data, $filters);
/** /**
* 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($input["redirect_uri"], OAUTH2_ERROR_INVALID_CLIENT, null, null, $input["state"]);
$input["redirect_uri"], OAUTH2_ERROR_INVALID_CLIENT, NULL, NULL, $input["state"] }
);
// We don't have a good URI to use // We don't have a good URI to use
$this->errorJsonResponse(OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_CLIENT); $this->errorJsonResponse(OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_CLIENT);
} }
@@ -424,68 +416,71 @@ class API_OAuth2_Adapter extends OAuth2
* redirect_uri is not required if already established via other channels * redirect_uri is not required if already established via other channels
* check an existing redirect URI against the one supplied * check an existing redirect URI against the one supplied
*/ */
$redirect_uri = $this->getRedirectUri($input["client_id"]); $redirectUri = $this->getRedirectUri($input["client_id"]);
/** /**
* 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 ( ! $redirectUri && ! $input["redirect_uri"]) {
$this->errorJsonResponse( $this->errorJsonResponse(OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_REQUEST);
OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_REQUEST); }
/** /**
* getRedirectUri() should return FALSE if the given client ID is invalid * getRedirectUri() should return false if the given client ID is invalid
* this probably saves us from making a separate db call, and simplifies the method set * this probably saves us from making a separate db call, and simplifies the method set
*/ */
if ($redirect_uri === FALSE) if ($redirectUri === false) {
$this->errorDoRedirectUriCallback( $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_CLIENT, null, null, $input["state"]);
$input["redirect_uri"], OAUTH2_ERROR_INVALID_CLIENT, NULL, NULL, $input["state"]); }
/** /**
* If there's an existing uri and one from input, verify that they match * If there's an existing uri and one from input, verify that they match
*/ */
if ($redirect_uri && $input["redirect_uri"]) { if ($redirectUri && $input["redirect_uri"]) {
/** /**
* Ensure that the input uri starts with the stored uri * Ensure that the input uri starts with the stored uri
*/ */
$compare = strcasecmp( $compare = strcasecmp(
substr( substr(
$input["redirect_uri"], 0, strlen($redirect_uri) $input["redirect_uri"], 0, strlen($redirectUri)
), $redirect_uri); ), $redirectUri);
if ($compare !== 0) if ($compare !== 0) {
$this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_REDIRECT_URI_MISMATCH, NULL, NULL, $input["state"]); $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_REDIRECT_URI_MISMATCH, null, null, $input["state"]);
} elseif ($redirect_uri) { }
} elseif ($redirectUri) {
/** /**
* They did not provide a uri from input, so use the stored one * They did not provide a uri from input, so use the stored one
*/ */
$input["redirect_uri"] = $redirect_uri; $input["redirect_uri"] = $redirectUri;
} }
/** /**
* 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"]);
} }
/** /**
* Check requested auth response type against the list of supported types * Check requested auth response type against the list of supported types
*/ */
if (array_search($input["response_type"], $this->getSupportedAuthResponseTypes()) === FALSE) if (array_search($input["response_type"], $this->getSupportedAuthResponseTypes()) === false) {
$this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE, NULL, NULL, $input["state"]); $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE, null, null, $input["state"]);
}
/** /**
* Restrict clients to certain authorization response types * Restrict clients to certain authorization response types
*/ */
if ($this->checkRestrictedAuthResponseType($input["client_id"], $input["response_type"]) === FALSE) if ($this->checkRestrictedAuthResponseType($input["client_id"], $input["response_type"]) === false) {
$this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_UNAUTHORIZED_CLIENT, NULL, NULL, $input["state"]); $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_UNAUTHORIZED_CLIENT, null, null, $input["state"]);
}
/** /**
* 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"]);
}
/** /**
* at this point all params are ok * at this point all params are ok
@@ -496,150 +491,131 @@ class API_OAuth2_Adapter extends OAuth2
} }
/** /**
* @param User $user
* *
* @param User $user * @return mixed
* @return API_OAuth2_Account * @throws logicalException
*/ */
public function updateAccount(User $user) public function updateAccount(User $user)
{ {
if ($this->client === null) if ($this->client === null) {
throw new logicalException("Client property must be set before update an account"); throw new logicalException("Client property must be set before update an account");
}
try { if (null === $account = $this->app['repo.api-accounts']->findByUserAndApplication($user, $this->client)) {
$account = API_OAuth2_Account::load_with_user($this->app, $this->client, $user); $account = $this->app['manipulator.api-account']->create($this->client, $user);
} catch (\Exception $e) {
$account = $this->createAccount($user->getId());
} }
return $account; return $account;
} }
/** /**
* @param $is_authorized
* @param array $params
* *
* @param int $usr_id * @return array
* @return API_OAuth2_Account
*/
private function createAccount($usr_id)
{
$user = $this->app['repo.users']->find($usr_id);
return API_OAuth2_Account::create($this->app, $user, $this->client);
}
/**
*
* @param <type> $is_authorized
* @param array $params
* @return string
*/ */
public function finishNativeClientAuthorization($is_authorized, $params = []) public function finishNativeClientAuthorization($is_authorized, $params = [])
{ {
$result = []; $result = [];
$params += [ $params += ['scope' => null, 'state' => null,];
'scope' => NULL,
'state' => NULL,
];
extract($params);
if ($state !== NULL) if ($params['state'] !== null) {
$result["query"]["state"] = $state; $result["query"]["state"] = $params['state'] ;
}
if ($is_authorized === FALSE) { if ($is_authorized === false) {
$result["error"] = OAUTH2_ERROR_USER_DENIED; $result["error"] = OAUTH2_ERROR_USER_DENIED;
} else { } else {
if ($response_type == OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE) if ($params['response_type'] === OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE) {
$result["code"] = $this->createAuthCode($account_id, $redirect_uri, $scope); $result["code"] = $this->createAuthCode($params['account_id'], $params['redirect_uri'], $params['scope']);
}
if ($response_type == OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN) if ($params['response_type'] === OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN) {
$result["error"] = OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE; $result["error"] = OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE;
}
} }
return $result; return $result;
} }
/** /**
* @param $redirectUri
* *
* @param <type> $redirect_uri * @return bool
* @return <type>
*/ */
public function isNativeApp($redirect_uri) public function isNativeApp($redirectUri)
{ {
return $redirect_uri === API_OAuth2_Application::NATIVE_APP_REDIRECT_URI; return $redirectUri === ApiApplication::NATIVE_APP_REDIRECT_URI;
} }
public function remember_this_ses_id($ses_id) public function rememberSession(Session $session)
{ {
try { if (null !== $token = $this->app['repo.api-oauth-tokens']->find($this->token)) {
$token = API_OAuth2_Token::load_by_oauth_token($this->app, $this->token); $this->app['manipulator.api-oauth-token']->rememberSessionId($token, $session->getId());
$token->set_session_id($ses_id);
return true;
} catch (\Exception $e) {
} }
return false;
} }
public function verifyAccessToken($scope = NULL, $exit_not_present = TRUE, $exit_invalid = TRUE, $exit_expired = TRUE, $exit_scope = TRUE, $realm = NULL) public function verifyAccessToken($scope = null, $exit_not_present = true, $exit_invalid = true, $exit_expired = true, $exit_scope = true, $realm = null)
{ {
$token_param = $this->getAccessTokenParams(); $token_param = $this->getAccessTokenParams();
// Access token was not provided // Access token was not provided
if ($token_param === false) { if ($token_param === 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; 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']) {
return $exit_invalid ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_UNAUTHORIZED, $realm, OAUTH2_ERROR_INVALID_TOKEN, 'End user has revoked access to his personal datas for your application.', NULL, $scope) : FALSE; return $exit_invalid ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_UNAUTHORIZED, $realm, OAUTH2_ERROR_INVALID_TOKEN, 'End user has revoked access to his personal datas for your application.', null, $scope) : false;
} }
if ($this->enable_expire) { if ($this->enable_expire) {
// 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
$this->session_id = $token['session_id']; $this->session_id = $token['session_id'];
$this->usr_id = $token['usr_id'];
$this->token = $token['oauth_token']; $this->token = $token['oauth_token'];
return TRUE; return true;
} }
public function finishClientAuthorization($is_authorized, $params = []) public function finishClientAuthorization($is_authorized, $params = [])
{ {
$params += [ $params += [
'scope' => NULL, 'scope' => null,
'state' => NULL, 'state' => null,
]; ];
extract($params);
if ($state !== NULL) if ($params['state'] !== null) {
$result["query"]["state"] = $state; $result["query"]["state"] = $params['state'];
if ($is_authorized === FALSE)
$result["query"]["error"] = OAUTH2_ERROR_USER_DENIED;
else {
if ($response_type == OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE || $response_type == OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN)
$result["query"]["code"] = $this->createAuthCode($account_id, $redirect_uri, $scope);
if ($response_type == OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN || $response_type == OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN)
$result["fragment"] = $this->createAccessToken($account_id, $scope);
} }
$this->doRedirectUriCallback($redirect_uri, $result); if ($is_authorized === false) {
$result["query"]["error"] = OAUTH2_ERROR_USER_DENIED;
} else {
if ($params['response_type'] == OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE || $params['response_type'] == OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN) {
$result["query"]["code"] = $this->createAuthCode($params['account_id'], $params['redirect_uri'], $params['scope']);
}
if ($params['response_type'] == OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN || $params['response_type'] == OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN) {
$result["fragment"] = $this->createAccessToken($params['account_id'], $params['scope']);
}
}
$this->doRedirectUriCallback($params['redirect_uri'], $result);
} }
/** /**
@@ -648,62 +624,75 @@ class API_OAuth2_Adapter extends OAuth2
public function grantAccessToken() public function grantAccessToken()
{ {
$filters = [ $filters = [
"grant_type" => ["filter" => FILTER_VALIDATE_REGEXP, "options" => ["regexp" => OAUTH2_GRANT_TYPE_REGEXP], "flags" => FILTER_REQUIRE_SCALAR], "grant_type" => [
"scope" => ["flags" => FILTER_REQUIRE_SCALAR], "filter" => FILTER_VALIDATE_REGEXP,
"code" => ["flags" => FILTER_REQUIRE_SCALAR], "options" => ["regexp" => OAUTH2_GRANT_TYPE_REGEXP],
"redirect_uri" => ["filter" => FILTER_SANITIZE_URL], "flags" => FILTER_REQUIRE_SCALAR
"username" => ["flags" => FILTER_REQUIRE_SCALAR], ],
"password" => ["flags" => FILTER_REQUIRE_SCALAR], "scope" => ["flags" => FILTER_REQUIRE_SCALAR],
"assertion_type" => ["flags" => FILTER_REQUIRE_SCALAR], "code" => ["flags" => FILTER_REQUIRE_SCALAR],
"assertion" => ["flags" => FILTER_REQUIRE_SCALAR], "redirect_uri" => ["filter" => FILTER_SANITIZE_URL],
"username" => ["flags" => FILTER_REQUIRE_SCALAR],
"password" => ["flags" => FILTER_REQUIRE_SCALAR],
"assertion_type" => ["flags" => FILTER_REQUIRE_SCALAR],
"assertion" => ["flags" => FILTER_REQUIRE_SCALAR],
"refresh_token" => ["flags" => FILTER_REQUIRE_SCALAR], "refresh_token" => ["flags" => FILTER_REQUIRE_SCALAR],
]; ];
$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
$client = $this->getClientCredentials(); $client = $this->getClientCredentials();
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);
}
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:
$application = API_OAuth2_Application::load_from_client_id($this->app, $client[0]); $application = ApiApplication::load_from_client_id($this->app, $client[0]);
if ( ! $application->is_password_granted()) { if ( ! $application->is_password_granted()) {
$this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE, 'Password grant type is not enable for your client'); $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE, 'Password grant type is not enable for your client');
} }
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"]);
@@ -712,26 +701,31 @@ class API_OAuth2_Adapter extends OAuth2
} }
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"]);
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);
}
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"]);
if ($stored === NULL || $client[0] != $stored["client_id"]) if ($stored === null || $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);
}
// store the refresh token locally so we can delete it when a new refresh token is generated // store the refresh token locally so we can delete it when a new refresh token is generated
$this->setVariable('_old_refresh_token', $stored["token"]); $this->setVariable('_old_refresh_token', $stored["token"]);
@@ -740,16 +734,19 @@ class API_OAuth2_Adapter extends OAuth2
case OAUTH2_GRANT_TYPE_NONE: case OAUTH2_GRANT_TYPE_NONE:
$stored = $this->checkNoneAccess($client[0]); $stored = $this->checkNoneAccess($client[0]);
if ($stored === FALSE) if ($stored === false) {
$this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST); $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST);
}
} }
// 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"]);
$this->sendJsonHeaders(); $this->sendJsonHeaders();
@@ -759,51 +756,64 @@ class API_OAuth2_Adapter extends OAuth2
return; return;
} }
protected function createAccessToken($account_id, $scope = NULL) protected function createAccessToken($accountId, $scope = null)
{ {
$token = [ $token = [
"access_token" => $this->genAccessToken(), "access_token" => $this->genAccessToken(),
"scope" => $scope "scope" => $scope
]; ];
if ($this->enable_expire) $expires = null;
if ($this->enable_expire) {
$token['expires_in'] = $this->getVariable('access_token_lifetime', OAUTH2_DEFAULT_ACCESS_TOKEN_LIFETIME); $token['expires_in'] = $this->getVariable('access_token_lifetime', OAUTH2_DEFAULT_ACCESS_TOKEN_LIFETIME);
}
$this->setAccessToken($token["access_token"], $account_id, time() + $this->getVariable('access_token_lifetime', OAUTH2_DEFAULT_ACCESS_TOKEN_LIFETIME), $scope); $this->setAccessToken($token["access_token"], $accountId, time() + $this->getVariable('access_token_lifetime', OAUTH2_DEFAULT_ACCESS_TOKEN_LIFETIME), $scope);
// Issue a refresh token also, if we support them // Issue a refresh token also, if we support them
if (in_array(OAUTH2_GRANT_TYPE_REFRESH_TOKEN, $this->getSupportedGrantTypes())) { if (in_array(OAUTH2_GRANT_TYPE_REFRESH_TOKEN, $this->getSupportedGrantTypes())) {
$token["refresh_token"] = $this->genAccessToken(); $token["refresh_token"] = $this->genAccessToken();
$this->setRefreshToken($token["refresh_token"], $account_id, time() + $this->getVariable('refresh_token_lifetime', OAUTH2_DEFAULT_REFRESH_TOKEN_LIFETIME), $scope); $this->setRefreshToken($token["refresh_token"], $accountId, time() + $this->getVariable('refresh_token_lifetime', OAUTH2_DEFAULT_REFRESH_TOKEN_LIFETIME), $scope);
// If we've granted a new refresh token, expire the old one // If we've granted a new refresh token, expire the old one
if ($this->getVariable('_old_refresh_token')) if ($this->getVariable('_old_refresh_token')) {
$this->unsetRefreshToken($this->getVariable('_old_refresh_token')); $this->unsetRefreshToken($this->getVariable('_old_refresh_token'));
}
} }
return $token; return $token;
} }
protected function checkUserCredentials($client_id, $username, $password) /**
* @param $clientId
* @param $username
* @param $password
*
* @return array|boolean
*/
protected function checkUserCredentials($clientId, $username, $password)
{ {
try { try {
$this->setClient(API_OAuth2_Application::load_from_client_id($this->app, $client_id)); if (null === $client = $this->app['repo.api-applications']->findByClientId($clientId)) {
return false;
}
$this->setClient($client);
$usr_id = $this->app['auth.native']->getUsrId($username, $password, Request::createFromGlobals()); $usrId = $this->app['auth.native']->getUsrId($username, $password, Request::createFromGlobals());
if (!$usr_id) { if (!$usrId) {
return false; return false;
} }
if (null === $user = $this->app['repo.users']->find($usr_id)) { if (null === $user = $this->app['repo.users']->find($usrId)) {
return false; return false;
} }
$account = $this->updateAccount($user); $account = $this->updateAccount($user);
return [ return [
'redirect_uri' => $this->client->get_redirect_uri() 'redirect_uri' => $this->client->getRedirectUri(),
, 'client_id' => $this->client->get_client_id() 'client_id' => $this->client->getClient(),
, 'account_id' => $account->get_id() 'account_id' => $account->getId(),
]; ];
} catch (AccountLockedException $e) { } catch (AccountLockedException $e) {
return false; return false;

View File

@@ -1,718 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class API_OAuth2_Application
{
/**
* constant for desktop application
*/
const DESKTOP_TYPE = 'desktop';
/**
* constant for web application
*/
const WEB_TYPE = 'web';
/**
* Uniform Resource Name
*/
const NATIVE_APP_REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob";
/**
*
* @var Application
*/
protected $app;
/**
*
* @var int
*/
protected $id;
/**
*
* @var User
*/
protected $creator;
/**
*
* @var string
*/
protected $type;
/**
*
* @var string
*/
protected $name;
/**
*
* @var string
*/
protected $nonce;
/**
*
* @var string
*/
protected $description;
/**
*
* @var string
*/
protected $website;
/**
*
* @var DateTime
*/
protected $created_on;
/**
*
* @var DateTime
*/
protected $last_modified;
/**
*
* @var string
*/
protected $client_id;
/**
*
* @var string
*/
protected $client_secret;
/**
*
* @var string
*/
protected $redirect_uri;
/**
*
* @var boolean
*/
protected $activated;
/**
*
* @var boolean
*/
protected $grant_password;
/**
*
* @param Application $app
* @param int $application_id
* @return API_OAuth2_Application
*/
public function __construct(Application $app, $application_id)
{
$this->app = $app;
$this->id = (int) $application_id;
$sql = '
SELECT
application_id, creator, type, name, description, website
, created_on, last_modified, client_id, client_secret, nonce
, redirect_uri, activated, grant_password
FROM api_applications
WHERE application_id = :application_id';
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':application_id' => $this->id]);
if (0 === $stmt->rowCount()) {
throw new NotFoundHttpException(sprintf('Application with id %d not found', $this->id));
}
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$this->creator = ! $row['creator'] ? null : $this->app['repo.users']->find($row['creator']);
$this->type = $row['type'];
$this->name = $row['name'];
$this->description = $row['description'];
$this->website = $row['website'];
$this->created_on = new DateTime($row['created_on']);
$this->last_modified = new DateTime($row['last_modified']);
$this->client_id = $row['client_id'];
$this->client_secret = $row['client_secret'];
$this->redirect_uri = $row['redirect_uri'];
$this->nonce = $row['nonce'];
$this->activated = ! ! $row['activated'];
$this->grant_password = ! ! $row['grant_password'];
return $this;
}
/**
*
* @return int
*/
public function get_id()
{
return $this->id;
}
/**
*
* @return User
*/
public function get_creator()
{
return $this->creator;
}
/**
*
* @return string
*/
public function get_type()
{
return $this->type;
}
/**
*
* @return string
*/
public function get_nonce()
{
return $this->nonce;
}
/**
*
* @param string $type
* @return API_OAuth2_Application
*/
public function set_type($type)
{
if ( ! in_array($type, [self::DESKTOP_TYPE, self::WEB_TYPE]))
throw new Exception_InvalidArgument();
$this->type = $type;
if ($this->type == self::DESKTOP_TYPE)
$this->set_redirect_uri(self::NATIVE_APP_REDIRECT_URI);
$sql = 'UPDATE api_applications SET type = :type, last_modified = NOW()
WHERE application_id = :application_id';
$params = [
':type' => $this->type
, ':application_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
*
* @return string
*/
public function get_name()
{
return $this->name;
}
/**
*
* @param string $name
* @return API_OAuth2_Application
*/
public function set_name($name)
{
$this->name = $name;
$sql = 'UPDATE api_applications SET name = :name, last_modified = NOW()
WHERE application_id = :application_id';
$params = [
':name' => $this->name
, ':application_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
*
* @return string
*/
public function get_description()
{
return $this->description;
}
/**
*
* @param string $description
* @return API_OAuth2_Application
*/
public function set_description($description)
{
$this->description = $description;
$sql = 'UPDATE api_applications
SET description = :description, last_modified = NOW()
WHERE application_id = :application_id';
$params = [
':description' => $this->description
, ':application_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
*
* @return string
*/
public function get_website()
{
return $this->website;
}
/**
*
* @param string $website
* @return API_OAuth2_Application
*/
public function set_website($website)
{
$this->website = $website;
$sql = 'UPDATE api_applications
SET website = :website, last_modified = NOW()
WHERE application_id = :application_id';
$params = [
':website' => $this->website
, ':application_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
* Tell wether application is activated
* @return boolean
*/
public function is_activated()
{
return $this->activated;
}
/**
*
* @param boolean $activated
* @return API_OAuth2_Application
*/
public function set_activated($activated)
{
$this->activated = $activated;
$sql = 'UPDATE api_applications
SET activated = :activated, last_modified = NOW()
WHERE application_id = :application_id';
$params = [
':activated' => $this->activated
, ':application_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
* Tell wether application authorize password grant type
* @return boolean
*/
public function is_password_granted()
{
return $this->grant_password;
}
/**
*
* @param boolean $grant
* @return API_OAuth2_Application
*/
public function set_grant_password($grant)
{
$this->grant_password = ! ! $grant;
$sql = 'UPDATE api_applications
SET grant_password = :grant_password, last_modified = NOW()
WHERE application_id = :application_id';
$params = [
':grant_password' => $this->grant_password
, ':application_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
*
* @return DateTime
*/
public function get_created_on()
{
return $this->created_on;
}
/**
*
* @return DateTime
*/
public function get_last_modified()
{
return $this->last_modified;
}
/**
*
* @return int
*/
public function get_client_id()
{
return $this->client_id;
}
/**
*
* @param int $client_id
* @return API_OAuth2_Application
*/
public function set_client_id($client_id)
{
$this->client_id = $client_id;
$sql = 'UPDATE api_applications
SET client_id = :client_id, last_modified = NOW()
WHERE application_id = :application_id';
$params = [
':client_id' => $this->client_id
, ':application_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
*
* @return string
*/
public function get_client_secret()
{
return $this->client_secret;
}
/**
*
* @param string $client_secret
* @return API_OAuth2_Application
*/
public function set_client_secret($client_secret)
{
$this->client_secret = $client_secret;
$sql = 'UPDATE api_applications
SET client_secret = :client_secret, last_modified = NOW()
WHERE application_id = :application_id';
$params = [
':client_secret' => $this->client_secret
, ':application_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
*
* @return string
*/
public function get_redirect_uri()
{
return $this->redirect_uri;
}
/**
*
* @param string $redirect_uri
* @return API_OAuth2_Application
*/
public function set_redirect_uri($redirect_uri)
{
$this->redirect_uri = $redirect_uri;
$sql = 'UPDATE api_applications
SET redirect_uri = :redirect_uri, last_modified = NOW()
WHERE application_id = :application_id';
$params = [
':redirect_uri' => $this->redirect_uri
, ':application_id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return $this;
}
/**
*
* @param User $user
* @return API_OAuth2_Account
*/
public function get_user_account(User $user)
{
$sql = 'SELECT api_account_id FROM api_accounts
WHERE usr_id = :usr_id AND application_id = :id';
$params = [
':usr_id' => $user->getId()
, ':id' => $this->id
];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if ( ! $row)
throw new NotFoundHttpException('Application not found.');
return new API_OAuth2_Account($this->app, $row['api_account_id']);
}
/**
*
* @return void
*/
public function delete()
{
foreach ($this->get_related_accounts() as $account) {
$account->delete();
}
$sql = 'DELETE FROM api_applications
WHERE application_id = :application_id';
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':application_id' => $this->get_id()]);
$stmt->closeCursor();
return;
}
/**
*
* @return array
*/
protected function get_related_accounts()
{
$sql = 'SELECT api_account_id FROM api_accounts
WHERE application_id = :application_id';
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':application_id' => $this->get_id()]);
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$accounts = [];
foreach ($rs as $row) {
$accounts[] = new API_OAuth2_Account($this->app, $row['api_account_id']);
}
return $accounts;
}
/**
*
* @param Application $app
* @param User $user
* @param type $name
* @return API_OAuth2_Application
*/
public static function create(Application $app, User $user = null, $name)
{
$sql = '
INSERT INTO api_applications (
application_id, creator, created_on, name, last_modified,
nonce, client_id, client_secret, activated, grant_password
)
VALUES (
null, :usr_id, NOW(), :name, NOW(), :nonce, :client_id,
:client_secret, :activated, :grant_password
)';
$nonce = $app['random.medium']->generateString(64);
$client_secret = $app['random.medium']->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS);
$client_token = $app['random.medium']->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS);
$params = [
':usr_id' => $user ? $user->getId() : null,
':name' => $name,
':client_id' => $client_token,
':client_secret' => $client_secret,
':nonce' => $nonce,
':activated' => 1,
':grant_password' => 0
];
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$application_id = $app['phraseanet.appbox']->get_connection()->lastInsertId();
$application = new self($app, $application_id);
if ($user) {
API_OAuth2_Account::create($app, $user, $application);
}
return $application;
}
/**
*
* @param Application $app
* @param type $client_id
* @return API_OAuth2_Application
*/
public static function load_from_client_id(Application $app, $client_id)
{
$sql = 'SELECT application_id FROM api_applications
WHERE client_id = :client_id';
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':client_id' => $client_id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if ( ! $row)
throw new NotFoundHttpException('Client not found.');
return new self($app, $row['application_id']);
}
public static function load_dev_app_by_user(Application $app, User $user)
{
$sql = 'SELECT a.application_id
FROM api_applications a, api_accounts b
WHERE a.creator = :usr_id AND a.application_id = b.application_id';
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':usr_id' => $user->getId()]);
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$apps = [];
foreach ($rs as $row) {
$apps[] = new API_OAuth2_Application($app, $row['application_id']);
}
return $apps;
}
public static function load_app_by_user(Application $app, User $user)
{
$sql = 'SELECT a.application_id
FROM api_accounts a, api_applications c
WHERE usr_id = :usr_id AND c.application_id = a.application_id';
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':usr_id' => $user->getId()]);
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$apps = [];
foreach ($rs as $row) {
$apps[] = new API_OAuth2_Application($app, $row['application_id']);
}
return $apps;
}
public static function load_authorized_app_by_user(Application $app, User $user)
{
$sql = '
SELECT a.application_id
FROM api_accounts a, api_applications c
WHERE usr_id = :usr_id AND c.application_id = a.application_id
AND revoked = 0';
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':usr_id' => $user->getId()]);
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$apps = [];
foreach ($rs as $row) {
$apps[] = new API_OAuth2_Application($app, $row['application_id']);
}
return $apps;
}
}

View File

@@ -1,180 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class API_OAuth2_AuthCode
{
protected $app;
protected $code;
protected $account;
protected $account_id;
protected $redirect_uri;
protected $expires;
protected $scope;
public function __construct(Application $app, $code)
{
$this->app = $app;
$this->code = $code;
$sql = 'SELECT code, api_account_id, redirect_uri, UNIX_TIMESTAMP(expires) AS expires, scope
FROM api_oauth_codes WHERE code = :code';
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':code' => $this->code]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if ( ! $row)
throw new NotFoundHttpException('Code not found');
$this->account_id = (int) $row['api_account_id'];
$this->redirect_uri = $row['redirect_uri'];
$this->expires = $row['expires'];
$this->scope = $row['scope'];
return $this;
}
public function get_code()
{
return $this->code;
}
/**
*
* @return API_OAuth2_Account
*/
public function get_account()
{
if ( ! $this->account)
$this->account = new API_OAuth2_Account($this->app, $this->account_id);
return $this->account;
}
public function get_redirect_uri()
{
return $this->redirect_uri;
}
public function set_redirect_uri($redirect_uri)
{
$sql = 'UPDATE api_oauth_codes SET redirect_uri = :redirect_uri
WHERE code = :code';
$params = [':redirect_uri' => $redirect_uri, ':code' => $this->code];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$this->redirect_uri = $redirect_uri;
return $this;
}
/**
*
* @return int
*/
public function get_expires()
{
return $this->expires;
}
public function get_scope()
{
return $this->scope;
}
public function set_scope($scope)
{
$sql = 'UPDATE api_oauth_codes SET scope = :scope
WHERE code = :code';
$params = [':scope' => $scope, ':code' => $this->code];
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$this->scope = $scope;
return $this;
}
public function delete()
{
$sql = 'DELETE FROM api_oauth_codes WHERE code = :code';
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':code' => $this->code]);
$stmt->closeCursor();
return;
}
/**
*
* @param Application $app
* @param API_OAuth2_Account $account
* @return array
*/
public static function load_codes_by_account(Application $app, API_OAuth2_Account $account)
{
$sql = 'SELECT code FROM api_oauth_codes
WHERE api_account_id = :account_id';
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$params = [":account_id" => $account->get_id()];
$stmt->execute($params);
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$codes = [];
foreach ($rs as $row) {
$codes[] = new API_OAuth2_AuthCode($app, $row['code']);
}
return $codes;
}
/**
*
* @param Application $app
* @param API_OAuth2_Account $account
* @param type $code
* @param int $expires
* @return API_OAuth2_AuthCode
*/
public static function create(Application $app, API_OAuth2_Account $account, $code, $expires)
{
$sql = 'INSERT INTO api_oauth_codes (code, api_account_id, expires)
VALUES (:code, :account_id, FROM_UNIXTIME(:expires))';
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$params = [
":code" => $code,
":account_id" => $account->get_id(),
":expires" => $expires
];
$stmt->execute($params);
$stmt->closeCursor();
return new self($app, $code);
}
}

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Constraints; use Symfony\Component\Validator\Constraints;
@@ -48,9 +49,7 @@ class API_OAuth2_Form_DevAppDesktop
public $urlwebsite; public $urlwebsite;
/** /**
* * @param Request $request
* @param Request $request
* @return API_OAuth2_Form_DevApp
*/ */
public function __construct(Request $request) public function __construct(Request $request)
{ {
@@ -58,8 +57,8 @@ class API_OAuth2_Form_DevAppDesktop
$this->description = $request->get('description', ''); $this->description = $request->get('description', '');
$this->scheme_website = $request->get('scheme-website', 'http://'); $this->scheme_website = $request->get('scheme-website', 'http://');
$this->website = $request->get('website', ''); $this->website = $request->get('website', '');
$this->callback = API_OAuth2_Application::NATIVE_APP_REDIRECT_URI; $this->callback = ApiApplication::NATIVE_APP_REDIRECT_URI;
$this->type = API_OAuth2_Application::DESKTOP_TYPE; $this->type = ApiApplication::DESKTOP_TYPE;
$this->urlwebsite = $this->scheme_website . $this->website; $this->urlwebsite = $this->scheme_website . $this->website;

View File

@@ -9,6 +9,7 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Constraints; use Symfony\Component\Validator\Constraints;
@@ -44,9 +45,7 @@ class API_OAuth2_Form_DevAppInternet
public $urlcallback; public $urlcallback;
/** /**
* * @param Request $request
* @param Request $request
* @return API_OAuth2_Form_DevApp
*/ */
public function __construct(Request $request) public function __construct(Request $request)
{ {
@@ -56,10 +55,10 @@ class API_OAuth2_Form_DevAppInternet
$this->callback = $request->get('callback', ''); $this->callback = $request->get('callback', '');
$this->scheme_website = $request->get('scheme-website', 'http://'); $this->scheme_website = $request->get('scheme-website', 'http://');
$this->scheme_callback = $request->get('scheme-callback', 'http://'); $this->scheme_callback = $request->get('scheme-callback', 'http://');
$this->type = API_OAuth2_Application::WEB_TYPE; $this->type = ApiApplication::WEB_TYPE;
$this->urlwebsite = $this->scheme_website . $this->website; $this->urlwebsite = sprintf('%s%s', $this->scheme_website, $this->website);
$this->urlcallback = $this->scheme_callback . $this->callback; $this->urlcallback = sprintf('%s%s', $this->scheme_callback, $this->callback);
return $this; return $this;
} }

View File

@@ -1,139 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
class API_OAuth2_RefreshToken
{
protected $app;
protected $token;
protected $account_id;
protected $account;
protected $expires;
protected $scope;
public function __construct(Application $app, $token)
{
$this->app = $app;
$this->token = $token;
$sql = 'SELECT api_account_id, UNIX_TIMESTAMP(expires) AS expires, scope
FROM api_oauth_refresh_tokens WHERE refresh_token = :token';
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':token' => $this->token]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$this->account_id = (int) $row['api_account_id'];
$this->expires = $row['expires'];
$this->scope = $row['scope'];
return $this;
}
public function get_value()
{
return $this->token;
}
/**
*
* @return API_OAuth2_Account
*/
public function get_account()
{
if (! $this->account) {
$this->account = new API_OAuth2_Account($this->app, $this->account_id);
}
return $this->account;
}
/**
*
* @return int
*/
public function get_expires()
{
return $this->expires;
}
public function get_scope()
{
return $this->scope;
}
public function delete()
{
$sql = 'DELETE FROM api_oauth_refresh_tokens
WHERE refresh_token = :refresh_token';
$stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([":refresh_token" => $this->token]);
$stmt->closeCursor();
return;
}
/**
*
* @param Application $app
* @param API_OAuth2_Account $account
* @return array
*/
public static function load_by_account(Application $app, API_OAuth2_Account $account)
{
$sql = 'SELECT refresh_token FROM api_oauth_refresh_tokens
WHERE api_account_id = :account_id';
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$stmt->execute([':account_id' => $account->get_id()]);
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$tokens = [];
foreach ($rs as $row) {
$tokens[] = new API_OAuth2_RefreshToken($app, $row['refresh_token']);
}
return $tokens;
}
/**
*
* @param Application $app
* @param API_OAuth2_Account $account
* @param int $expires
* @param type $refresh_token
* @param type $scope
* @return API_OAuth2_RefreshToken
*/
public static function create(Application $app, API_OAuth2_Account $account, $expires, $refresh_token, $scope)
{
$sql = 'INSERT INTO api_oauth_refresh_tokens
(refresh_token, api_account_id, expires, scope)
VALUES (:refresh_token, :account_id, :expires, :scope)';
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$params = [
":refresh_token" => $refresh_token,
":account_id" => $account->get_id(),
":expires" => $expires,
":scope" => $scope
];
$stmt->execute($params);
$stmt->closeCursor();
return new self($app, $refresh_token);
}
}

View File

@@ -1,319 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use RandomLib\Generator;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class API_OAuth2_Token
{
/**
*
* @var appbox
*/
protected $appbox;
/**
*
* @var API_OAuth2_Account
*/
protected $account;
/**
*
* @var string
*/
protected $token;
/**
*
* @var int
*/
protected $session_id;
/**
*
* @var int
*/
protected $expires;
/**
*
* @var string
*/
protected $scope;
private $generator;
/**
*
* @param appbox $appbox
* @param API_OAuth2_Account $account
* @return API_OAuth2_Token
*/
public function __construct(appbox $appbox, API_OAuth2_Account $account, Generator $generator)
{
$this->appbox = $appbox;
$this->account = $account;
$this->generator = $generator;
$sql = 'SELECT oauth_token, session_id, UNIX_TIMESTAMP(expires) as expires, scope
FROM api_oauth_tokens
WHERE api_account_id = :account_id';
$stmt = $this->appbox->get_connection()->prepare($sql);
$stmt->execute([':account_id' => $this->account->get_id()]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ( ! $row)
throw new NotFoundHttpException('Account not found');
$stmt->closeCursor();
$this->token = $row['oauth_token'];
$this->session_id = is_null($row['session_id']) ? null : (int) $row['session_id'];
$this->expires = $row['expires'];
$this->scope = $row['scope'];
return $this;
}
/**
*
* @return string
*/
public function get_value()
{
return $this->token;
}
/**
*
* @param string $oauth_token
* @return API_OAuth2_Token
*/
public function set_value($oauth_token)
{
$sql = 'UPDATE api_oauth_tokens SET oauth_token = :oauth_token
WHERE oauth_token = :current_token';
$params = [
':oauth_token' => $oauth_token
, ':current_token' => $this->token
];
$stmt = $this->appbox->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$this->token = $oauth_token;
return $this;
}
/**
*
* @return int
*/
public function get_session_id()
{
return $this->session_id;
}
/**
*
* @param int $session_id
* @return API_OAuth2_Token
*/
public function set_session_id($session_id)
{
$sql = 'UPDATE api_oauth_tokens SET session_id = :session_id
WHERE oauth_token = :current_token';
$params = [
':session_id' => $session_id
, ':current_token' => $this->token
];
$stmt = $this->appbox->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$this->session_id = $session_id !== null ? (int) $session_id : $session_id;
return $this;
}
/**
*
* @return int
*/
public function get_expires()
{
return $this->expires;
}
/**
*
* @param int $expires
* @return API_OAuth2_Token
*/
public function set_expires($expires)
{
$sql = 'UPDATE api_oauth_tokens SET expires = FROM_UNIXTIME(:expires)
WHERE oauth_token = :oauth_token';
$params = [
':expires' => $expires
, ':oauth_token' => $this->get_value()
];
$stmt = $this->appbox->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$this->expires = $expires;
return $this;
}
/**
*
* @return string
*/
public function get_scope()
{
return $this->scope;
}
public function set_scope($scope)
{
$sql = 'UPDATE api_oauth_tokens SET scope = :scope
WHERE oauth_token = :oauth_token';
$params = [
':scope' => $scope
, ':oauth_token' => $this->get_value()
];
$stmt = $this->appbox->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$this->scope = $scope;
return $this;
}
/**
*
* @return API_OAuth2_Account
*/
public function get_account()
{
return $this->account;
}
/**
*
* @return API_OAuth2_Token
*/
public function renew()
{
$sql = 'UPDATE api_oauth_tokens SET oauth_token = :new_token
WHERE oauth_token = :old_token';
$new_token = $this->generator->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS);
$params = [
':new_token' => $new_token
, ':old_token' => $this->get_value()
];
$stmt = $this->appbox->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
$this->token = $new_token;
return $this;
}
/**
*
* @return void
*/
public function delete()
{
$sql = 'DELETE FROM api_oauth_tokens WHERE oauth_token = :oauth_token';
$stmt = $this->appbox->get_connection()->prepare($sql);
$stmt->execute([':oauth_token' => $this->get_value()]);
$stmt->closeCursor();
return;
}
/**
*
* @param Application $app
* @param string $oauth_token
* @return API_OAuth2_Token
*/
public static function load_by_oauth_token(Application $app, $oauth_token)
{
$sql = 'SELECT a.api_account_id
FROM api_oauth_tokens a, api_accounts b
WHERE a.oauth_token = :oauth_token
AND a.api_account_id = b.api_account_id';
$stmt = $app['phraseanet.appbox']->get_connection()->prepare($sql);
$params = [":oauth_token" => $oauth_token];
$stmt->execute($params);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if (!$row) {
throw new NotFoundHttpException('Account not found');
}
return new self($app['phraseanet.appbox'], new API_OAuth2_Account($app, $row['api_account_id']), $app['random.medium']);
}
/**
*
* @param appbox $appbox
* @param API_OAuth2_Account $account
* @param string $scope
* @return API_OAuth2_Token
*/
public static function create(appbox $appbox, API_OAuth2_Account $account, Generator $generator, $scope = null)
{
$sql = 'INSERT INTO api_oauth_tokens
(oauth_token, session_id, api_account_id, expires, scope)
VALUES (:token, null, :account_id, :expire, :scope)';
$expires = new \DateTime('+1 hour');
$params = [
':token' => $generator->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS)
, ':account_id' => $account->get_id()
, ':expire' => $expires->format(DATE_ISO8601)
, ':scope' => $scope
];
$stmt = $appbox->get_connection()->prepare($sql);
$stmt->execute($params);
$stmt->closeCursor();
return new API_OAuth2_Token($appbox, $account, $generator);
}
}

View File

@@ -251,6 +251,12 @@ abstract class base implements cache_cacheableInterface
$ORMTables = [ $ORMTables = [
'AuthFailures', 'AuthFailures',
'ApiApplications',
'ApiAccounts',
'ApiLogs',
'ApiOauthCodes',
'ApiOauthRefreshTokens',
'ApiOauthTokens',
'AggregateTokens', 'AggregateTokens',
'BasketElements', 'BasketElements',
'Baskets', 'Baskets',

View File

@@ -10,6 +10,7 @@
*/ */
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class patch_370alpha3a extends patchAbstract class patch_370alpha3a extends patchAbstract
@@ -42,7 +43,7 @@ class patch_370alpha3a extends patchAbstract
*/ */
public function getDoctrineMigrations() public function getDoctrineMigrations()
{ {
return []; return ['api'];
} }
/** /**
@@ -58,18 +59,21 @@ class patch_370alpha3a extends patchAbstract
*/ */
public function apply(base $appbox, Application $app) public function apply(base $appbox, Application $app)
{ {
try { if (null === $app['repo.api-applications']->findByClientId(\API_OAuth2_Application_Navigator::CLIENT_ID)) {
\API_OAuth2_Application::load_from_client_id($app, \API_OAuth2_Application_Navigator::CLIENT_ID); $application = $app['manipulator.api-application']->create(
} catch (NotFoundHttpException $e) { \API_OAuth2_Application_Navigator::CLIENT_NAME,
$client = \API_OAuth2_Application::create($app, null, \API_OAuth2_Application_Navigator::CLIENT_NAME); ApiApplication::DESKTOP_TYPE,
'',
'http://www.phraseanet.com',
null,
ApiApplication::NATIVE_APP_REDIRECT_URI
);
$client->set_activated(true); $application->setGrantPassword(true);
$client->set_grant_password(true); $application->setClientId(\API_OAuth2_Application_Navigator::CLIENT_ID);
$client->set_website("http://www.phraseanet.com"); $application->setClientSecret(\API_OAuth2_Application_Navigator::CLIENT_SECRET);
$client->set_client_id(\API_OAuth2_Application_Navigator::CLIENT_ID);
$client->set_client_secret(\API_OAuth2_Application_Navigator::CLIENT_SECRET); $app['manipulator.api-application']->update($application);
$client->set_type(\API_OAuth2_Application::DESKTOP_TYPE);
$client->set_redirect_uri(\API_OAuth2_Application::NATIVE_APP_REDIRECT_URI);
} }
return true; return true;

View File

@@ -10,6 +10,7 @@
*/ */
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class patch_3715alpha1a extends patchAbstract class patch_3715alpha1a extends patchAbstract
@@ -59,18 +60,21 @@ class patch_3715alpha1a extends patchAbstract
*/ */
public function apply(base $appbox, Application $app) public function apply(base $appbox, Application $app)
{ {
try { if (null === $app['repo.api-applications']->findByClientId(\API_OAuth2_Application_OfficePlugin::CLIENT_ID)) {
\API_OAuth2_Application::load_from_client_id($app, \API_OAuth2_Application_OfficePlugin::CLIENT_ID); $application = $app['manipulator.api-application']->create(
} catch (NotFoundHttpException $e) { \API_OAuth2_Application_OfficePlugin::CLIENT_NAME,
$client = \API_OAuth2_Application::create($app, null, \API_OAuth2_Application_OfficePlugin::CLIENT_NAME); ApiApplication::DESKTOP_TYPE,
'',
'http://www.phraseanet.com',
null,
ApiApplication::NATIVE_APP_REDIRECT_URI
);
$client->set_activated(true); $application->setGrantPassword(true);
$client->set_grant_password(true); $application->setClientId(\API_OAuth2_Application_OfficePlugin::CLIENT_ID);
$client->set_website("http://www.phraseanet.com"); $application->setClientSecret(\API_OAuth2_Application_OfficePlugin::CLIENT_SECRET);
$client->set_client_id(\API_OAuth2_Application_OfficePlugin::CLIENT_ID);
$client->set_client_secret(\API_OAuth2_Application_OfficePlugin::CLIENT_SECRET); $app['manipulator.api-application']->update($application);
$client->set_type(\API_OAuth2_Application::DESKTOP_TYPE);
$client->set_redirect_uri(\API_OAuth2_Application::NATIVE_APP_REDIRECT_URI);
} }
return true; return true;

View File

@@ -0,0 +1,246 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
use Doctrine\ORM\EntityManager;
class patch_390alpha17a extends patchAbstract
{
/** @var string */
private $release = '3.9.0-alpha.17';
/** @var array */
private $concern = [base::APPLICATION_BOX];
/**
* {@inheritdoc}
*/
public function get_release()
{
return $this->release;
}
/**
* {@inheritdoc}
*/
public function require_all_upgrades()
{
return false;
}
/**
* {@inheritdoc}
*/
public function concern()
{
return $this->concern;
}
/**
* {@inheritdoc}
*/
public function getDoctrineMigrations()
{
return ['api'];
}
/**
* {@inheritdoc}
*/
public function apply(base $appbox, Application $app)
{
$this->fillApplicationTable($app['EM']);
$this->fillAccountTable($app['EM']);
$this->fillLogTable($app['EM']);
$this->fillCodeTable($app['EM']);
$this->fillRefreshTokenTable($app['EM']);
$this->fillOauthTokenTable($app['EM']);
$this->setOauthTokenExpiresToNull($app['EM']);
$this->updateLogsTable($app['EM']);
}
private function fillApplicationTable(EntityManager $em)
{
if (false === $this->tableExists($em, 'api_applications')) {
return true;
}
$em->getConnection()->executeUpdate(
'INSERT INTO ApiApplications
(
id, `type`, `name`, description, website,
created, updated, client_id, client_secret, nonce,
redirect_uri, activated, grant_password, creator_id
)
(
SELECT
a.application_id, a.`type`, a.`name`, a.description, a.website,
a.created_on, a.last_modified, a.client_id, a.client_secret, a.nonce,
a.redirect_uri, a.activated, a.grant_password, creator
FROM api_applications a
LEFT JOIN Users u ON (u.id = a.creator)
WHERE u.id IS NOT NULL
OR a.`name` = "'. \API_OAuth2_Application_Navigator::CLIENT_NAME .'"
OR a.`name` = "'. \API_OAuth2_Application_OfficePlugin::CLIENT_NAME .'"
)'
);
}
private function fillAccountTable(EntityManager $em)
{
if (false === $this->tableExists($em, 'api_accounts')) {
return true;
}
$em->getConnection()->executeUpdate(
'INSERT INTO ApiAccounts
(
id, user_id, revoked,
api_version, created, application_id
)
(
SELECT
a.api_account_id, a.usr_id, a.revoked,
a.api_version, a.created, a.application_id
FROM api_accounts a
INNER JOIN Users ON (Users.id = a.usr_id)
INNER JOIN api_applications b ON (a.application_id = b.application_id)
)'
);
}
private function fillLogTable(EntityManager $em)
{
if (false === $this->tableExists($em, 'api_accounts')) {
return true;
}
$em->getConnection()->executeUpdate(
'INSERT INTO ApiLogs
(
id, account_id, route, error_message,
created, status_code, format, resource,
general, aspect, `action`, error_code
)
(
SELECT
a.api_log_id, a.api_account_id, a.api_log_route, a.api_log_error_message,
a.api_log_date, a.api_log_status_code, a.api_log_format, a.api_log_ressource,
a.api_log_general, a.api_log_aspect, a.api_log_action, a.api_log_error_code
FROM api_logs a
INNER JOIN api_accounts b ON (b.api_account_id = a.api_account_id)
)'
);
}
private function fillCodeTable(EntityManager $em)
{
if (false === $this->tableExists($em, 'api_oauth_codes')) {
return true;
}
$em->getConnection()->executeUpdate(
'INSERT INTO ApiOauthCodes
(
code, account_id, redirect_uri, expires,
scope, created, updated
)
(
SELECT
a.code, a.api_account_id, a.redirect_uri, a.expires,
a.scope, NOW(), NOW()
FROM api_oauth_codes a
INNER JOIN api_accounts b ON (b.api_account_id = a.api_account_id)
)'
);
}
private function fillRefreshTokenTable(EntityManager $em)
{
if (false === $this->tableExists($em, 'api_oauth_refresh_tokens')) {
return true;
}
$em->getConnection()->executeUpdate(
'INSERT INTO ApiOauthRefreshTokens
(
refresh_token, account_id, expires,
scope, created, updated
)
(
SELECT
a.refresh_token, a.api_account_id, a.expires,
a.scope, NOW(), NOW()
FROM api_oauth_refresh_tokens a
INNER JOIN api_accounts b ON (b.api_account_id = a.api_account_id)
)'
);
}
private function fillOauthTokenTable(EntityManager $em)
{
if (false === $this->tableExists($em, 'api_oauth_tokens')) {
return true;
}
$em->getConnection()->executeUpdate(
'INSERT INTO ApiOauthTokens
(
oauth_token, account_id, session_id, expires,
scope, created, updated, last_used
)
(
SELECT
a.oauth_token, a.api_account_id, a.session_id, expires,
a.scope, NOW(), NOW(), NOW()
FROM api_oauth_tokens a
INNER JOIN api_accounts b ON (b.api_account_id = a.api_account_id)
)'
);
}
private function setOauthTokenExpiresToNull(EntityManager $em)
{
$qb = $em->createQueryBuilder();
$q = $qb->update('Phraseanet:ApiOauthToken', 'a')
->set('a.expires', $qb->expr()->literal(null))
->getQuery();
$q->execute();
}
/**
* Update ApiLogs Table
*
* before :
* +--------------------+
* | route |
* +--------------------+
* | GET /databox/list/ |
* +--------------------+
*
* after :
* +----------------+--------+
* | route | method |
* +----------------+--------+
* | /databox/list/ | GET |
* +----------------+--------+
*
*/
private function updateLogsTable(EntityManager $em)
{
$em->getConnection()->executeUpdate("
UPDATE `ApiLogs`
SET method = SUBSTRING_INDEX(SUBSTRING_INDEX(route, ' ', 1), ' ', -1),
route = SUBSTRING_INDEX(SUBSTRING_INDEX(route, ' ', 2), ' ', -1)
");
}
}

View File

@@ -77,567 +77,6 @@
<engine>InnoDB</engine> <engine>InnoDB</engine>
</table> </table>
<!-- API -->
<table name="api_accounts">
<fields>
<field>
<name>api_account_id</name>
<type>int(11) unsigned</type>
<null></null>
<extra>auto_increment</extra>
<default></default>
<comment></comment>
</field>
<field>
<name>usr_id</name>
<type>int(11) unsigned</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>revoked</name>
<type>int(1)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_version</name>
<type>char(16)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>application_id</name>
<type>int(11)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>created</name>
<type>datetime</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
</fields>
<indexes>
<index>
<name>PRIMARY</name>
<type>PRIMARY</type>
<fields>
<field>api_account_id</field>
</fields>
</index>
<index>
<name>usr_id</name>
<type>INDEX</type>
<fields>
<field>usr_id</field>
</fields>
</index>
<index>
<name>application_id</name>
<type>INDEX</type>
<fields>
<field>application_id</field>
</fields>
</index>
</indexes>
<engine>InnoDB</engine>
</table>
<table name="api_applications">
<fields>
<field>
<name>application_id</name>
<type>int(11) unsigned</type>
<null></null>
<extra>auto_increment</extra>
<default></default>
<comment></comment>
</field>
<field>
<name>creator</name>
<type>int(11) unsigned</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>type</name>
<type>enum('web','desktop')</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>name</name>
<type>varchar(64)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>description</name>
<type>text</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>website</name>
<type>varchar(120)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>created_on</name>
<type>datetime</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>last_modified</name>
<type>datetime</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>client_id</name>
<type>char(128)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>client_secret</name>
<type>char(128)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>nonce</name>
<type>char(64)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>redirect_uri</name>
<type>varchar(128)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>activated</name>
<type>int(1)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>grant_password</name>
<type>int(1)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
</fields>
<indexes>
<index>
<name>PRIMARY</name>
<type>PRIMARY</type>
<fields>
<field>application_id</field>
</fields>
</index>
<index>
<name>creator</name>
<type>INDEX</type>
<fields>
<field>creator</field>
</fields>
</index>
<index>
<name>client_id</name>
<type>UNIQUE</type>
<fields>
<field>client_id</field>
</fields>
</index>
</indexes>
<defaults>
<default>
<data key="application_id">null</data>
<data key="creator">null</data>
<data key="type">desktop</data>
<data key="name">phraseanet-navigator</data>
<data key="description"></data>
<data key="website">http://www.phraseanet.com</data>
<data key="created_on">NOW()</data>
<data key="last_modified">NOW()</data>
<data key="client_id">\alchemy\phraseanet\id\4f981093aebb66.06844599</data>
<data key="client_secret">\alchemy\phraseanet\secret\4f9810d4b09799.51622662</data>
<data key="nonce">5b6lIf</data>
<data key="redirect_uri">urn:ietf:wg:oauth:2.0:oob</data>
<data key="activated">1</data>
<data key="grant_password">1</data>
</default>
<default>
<data key="application_id">null</data>
<data key="creator">null</data>
<data key="type">desktop</data>
<data key="name">office-plugin</data>
<data key="description"></data>
<data key="website">http://www.phraseanet.com</data>
<data key="created_on">NOW()</data>
<data key="last_modified">NOW()</data>
<data key="client_id">\alchemy\phraseanet\id\999585175b5fbb6e140efbdfea86c561</data>
<data key="client_secret">\alchemy\phraseanet\secret\6d53d0bc74e6c8c1a325541f71da1ea5</data>
<data key="nonce">AfCF61</data>
<data key="redirect_uri">urn:ietf:wg:oauth:2.0:oob</data>
<data key="activated">1</data>
<data key="grant_password">1</data>
</default>
</defaults>
<engine>InnoDB</engine>
</table>
<table name="api_logs">
<fields>
<field>
<name>api_log_id</name>
<type>int(11) unsigned</type>
<null></null>
<extra>auto_increment</extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_account_id</name>
<type>int(11) unsigned</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_log_route</name>
<type>varchar(256)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_log_date</name>
<type>datetime</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_log_status_code</name>
<type>int(11) unsigned</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_log_format</name>
<type>varchar(64)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_log_resource</name>
<type>varchar(64)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_log_general</name>
<type>varchar(64)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_log_aspect</name>
<type>varchar(64)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_log_action</name>
<type>varchar(64)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_log_error_code</name>
<type>int(11) unsigned</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_log_error_message</name>
<type>varchar(256)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
</fields>
<indexes>
<index>
<name>PRIMARY</name>
<type>PRIMARY</type>
<fields>
<field>api_log_id</field>
</fields>
</index>
<index>
<name>api_account_id</name>
<type>INDEX</type>
<fields>
<field>api_account_id</field>
</fields>
</index>
</indexes>
<engine>InnoDB</engine>
</table>
<table name="api_oauth_codes">
<fields>
<field>
<name>code</name>
<type>char(128)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_account_id</name>
<type>int(11) unsigned</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>redirect_uri</name>
<type>varchar(256)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>expires</name>
<type>DATETIME</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>scope</name>
<type>varchar(200)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
</fields>
<indexes>
<index>
<name>PRIMARY</name>
<type>PRIMARY</type>
<fields>
<field>code</field>
</fields>
</index>
<index>
<name>api_account_id</name>
<type>INDEX</type>
<fields>
<field>api_account_id</field>
</fields>
</index>
</indexes>
<engine>InnoDB</engine>
</table>
<table name="api_oauth_tokens">
<fields>
<field>
<name>oauth_token</name>
<type>char(128)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>session_id</name>
<type>int(6)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_account_id</name>
<type>int(11)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>expires</name>
<type>DATETIME</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>scope</name>
<type>varchar(200)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
</fields>
<indexes>
<index>
<name>PRIMARY</name>
<type>PRIMARY</type>
<fields>
<field>oauth_token</field>
</fields>
</index>
<index>
<name>api_account_id</name>
<type>INDEX</type>
<fields>
<field>api_account_id</field>
</fields>
</index>
<index>
<name>session_id</name>
<type>INDEX</type>
<fields>
<field>session_id</field>
</fields>
</index>
</indexes>
<engine>InnoDB</engine>
</table>
<table name="api_oauth_refresh_tokens">
<fields>
<field>
<name>refresh_token</name>
<type>char(128)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>api_account_id</name>
<type>int(11)</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>expires</name>
<type>DATETIME</type>
<null></null>
<extra></extra>
<default></default>
<comment></comment>
</field>
<field>
<name>scope</name>
<type>varchar(200)</type>
<null>YES</null>
<extra></extra>
<default></default>
<comment></comment>
</field>
</fields>
<indexes>
<index>
<name>PRIMARY</name>
<type>PRIMARY</type>
<fields>
<field>refresh_token</field>
</fields>
</index>
<index>
<name>api_account_id</name>
<type>INDEX</type>
<fields>
<field>api_account_id</field>
</fields>
</index>
</indexes>
<engine>InnoDB</engine>
</table>
<table name="bridge_accounts"> <table name="bridge_accounts">
<fields> <fields>
<field> <field>

View File

@@ -60,6 +60,9 @@ migrations:
migration19: migration19:
version: token version: token
class: Alchemy\Phrasea\Setup\DoctrineMigrations\TokenMigration class: Alchemy\Phrasea\Setup\DoctrineMigrations\TokenMigration
migration20:
version: api
class: Alchemy\Phrasea\Setup\DoctrineMigrations\ApiMigration
migration21: migration21:
version: aggregate-token version: aggregate-token
class: Alchemy\Phrasea\Setup\DoctrineMigrations\AggregateTokenMigration class: Alchemy\Phrasea\Setup\DoctrineMigrations\AggregateTokenMigration

View File

@@ -13,28 +13,30 @@
<h3>{{ "Vous avez autorise ces applications a acceder a votre compte" | trans }}</h3> <h3>{{ "Vous avez autorise ces applications a acceder a votre compte" | trans }}</h3>
{% if applications|length > 0 %} {% if applications|length > 0 %}
<ul class="unstyled app-list"> <ul class="unstyled app-list">
{% for application in applications %} {% for data in applications if data["user-account"] is not none %}
<li id="app_{{ application.get_id() }}"> {% set application = data["application"] %}
{% set account = data["user-account"] %}
<li id="app_{{ application.getId() }}">
<div> <div>
{% set account = application.get_user_account(app["authentication"].getUser()) %}
<a href="{{ path("grant_app_access", {"application_id" : application.get_id()}) }}" class="revoke app-btn btn btn-small pull-right {% if account.is_revoked() %}hidden{% endif %}" value="{{application.get_id()}}">{{ "Revoquer l\'access" | trans }}</a> <a href="{{ path("grant_app_access", {"application" : application.getId()}) }}" class="revoke app-btn btn btn-small pull-right {% if account.isRevoked() %}hidden{% endif %}" value="{{application.getId()}}">{{ "Revoquer l\'access" | trans }}</a>
<a href="{{ path("grant_app_access", {"application_id" : application.get_id()}) }}" class="authorize app-btn btn btn-small pull-right {% if not account.is_revoked() %}hidden{% endif %}" value="{{application.get_id()}}">{{ "Authoriser l\'access" | trans }}</a> <a href="{{ path("grant_app_access", {"application" : application.getId()}) }}" class="authorize app-btn btn btn-small pull-right {% if not account.isRevoked() %}hidden{% endif %}" value="{{application.getId()}}">{{ "Authoriser l\'access" | trans }}</a>
<p class="app-row"> <p class="app-row">
<a href="{{ application.get_website() }}" target="_blank"> <a href="{{ application.getWebsite() }}" target="_blank">
<strong>{{ application.get_name() }}</strong> <strong>{{ application.getName() }}</strong>
</a> </a>
{% if application.get_creator() is not none %} {% if application.getCreator() is not none %}
<small> <small>
{% set user_name = application.get_creator().getDisplayName() %} {% set user_name = application.getCreator().getDisplayName() %}
{% trans with {'%user_name%' : user_name} %}par %user_name%{% endtrans %} {% trans with {'%user_name%' : user_name} %}par %user_name%{% endtrans %}
</small> </small>
{% endif%} {% endif%}
</p> </p>
<p class="app-row"> <p class="app-row">
<span class="status text-error {% if account.is_revoked() == false %}hidden{% endif %}">{{ "Not Allowed" | trans }}</span> <span class="status text-error {% if account.isRevoked() == false %}hidden{% endif %}">{{ "Not Allowed" | trans }}</span>
<span class="status text-success {% if account.is_revoked() == true %}hidden{% endif %}">{{ "Allowed" | trans }}</span> <span class="status text-success {% if account.isRevoked() == true %}hidden{% endif %}">{{ "Allowed" | trans }}</span>
</p> </p>
<p class="app-row">{{ application.get_description()|truncate(120, true, "...") }}</p> <p class="app-row">{{ application.getDescription()|truncate(120, true, "...") }}</p>
</div> </div>
</li> </li>
{%endfor%} {%endfor%}

View File

@@ -37,7 +37,7 @@
<h1 id="namePhr">{{ app['conf'].get(['registry', 'general', 'title']) }}</h1> <h1 id="namePhr">{{ app['conf'].get(['registry', 'general', 'title']) }}</h1>
</div> </div>
{% if user is not none %} {% if app['authentication'].getUser() is not none %}
{% set username = '<b>' ~ app['authentication'].getUser().getDisplayName() ~ '</b>' %} {% set username = '<b>' ~ app['authentication'].getUser().getDisplayName() ~ '</b>' %}
<div id="hello-box" class="span6 offset3"> <div id="hello-box" class="span6 offset3">
<p class="login_hello"> <p class="login_hello">

View File

@@ -10,11 +10,11 @@
<div class="row-fluid"> <div class="row-fluid">
<div class="span12"> <div class="span12">
<h1>{{ "Application" | trans }}</h1> <h1>{{ "Application" | trans }}</h1>
<input type="hidden" value="{{ application.get_id() }}" name="app_id"/> <input type="hidden" value="{{ application.getId() }}" name="app_id"/>
<div> <div>
<div><strong><a class="link" href="{{ path("developers_application", {"id" : application.get_id()}) }}">{{ application.get_name() }}</a></strong></div> <div><strong><a class="link" href="{{ path("developers_application", {"application" : application.getId()}) }}">{{ application.getName() }}</a></strong></div>
<div>{{ application.get_description() }}</div> <div>{{ application.getDescription() }}</div>
</div> </div>
<h1>{{ "settings OAuth" | trans }}</h1> <h1>{{ "settings OAuth" | trans }}</h1>
@@ -24,22 +24,22 @@
<tbody> <tbody>
<tr> <tr>
<td>Client ID</td> <td>Client ID</td>
<td>{{ application.get_client_id() }}</td> <td>{{ application.getClientId() }}</td>
</tr> </tr>
<tr> <tr>
<td>Client Secret</td> <td>Client Secret</td>
<td>{{ application.get_client_secret() }}</td> <td>{{ application.getClientSecret() }}</td>
</tr> </tr>
<tr> <tr>
<td>{{ "URL de callback" | trans }}</td> <td>{{ "URL de callback" | trans }}</td>
{% if application.get_type() == constant("API_OAuth2_Application::DESKTOP_TYPE") %} {% if application.getType() == constant("DESKTOP_TYPE", application) %}
<td> <td>
<span>{{ application.get_redirect_uri() }}</span> <span>{{ application.getRedirectUri() }}</span>
</td> </td>
{% else %} {% else %}
<td class="url_callback"> <td class="url_callback">
<span class="url_callback_input">{{ application.get_redirect_uri() }}</span> <span class="url_callback_input">{{ application.getRedirectUri() }}</span>
<a href="{{ path("submit_application_callback", {"id" : application.get_id()}) }}" class="save_callback btn btn-small btn-info" style="display:none;"> <a href="{{ path("submit_application_callback", {"application" : application.getId()}) }}" class="save_callback btn btn-small btn-info" style="display:none;">
{{ "Save" | trans }} {{ "Save" | trans }}
</a> </a>
<button type="button" class="modifier_callback btn btn-small"> <button type="button" class="modifier_callback btn btn-small">
@@ -61,9 +61,9 @@
<td> <td>
<input class="grant-type" <input class="grant-type"
type="checkbox" type="checkbox"
{{ application.is_password_granted() ? "checked='checked'" : "" }} {{ application.isPasswordGranted() ? "checked='checked'" : "" }}
name="grant" name="grant"
value="{{ path("submit_developers_application_authorize_grant_password", {"id" : application.get_id()}) }}" value="{{ path("submit_developers_application_authorize_grant_password", {"application" : application.getId()}) }}"
> >
</td> </td>
</tr> </tr>
@@ -81,18 +81,18 @@
</td> </td>
<td> <td>
<span id="my_access_token"> <span id="my_access_token">
{% if not token is none %} {% if not token is none %}
{{ token|default("") }} {{ token.getOauthToken()|default("") }}
{% else %} {% else %}
{{ "Le token n\'a pas encore ete genere" | trans }} {{ "Le token n\'a pas encore ete genere" | trans }}
{% endif %} {% endif %}
</span> </span>
<a id="generate_access" href="{{ path("submit_developers_application_token",{ "id" : application.get_id()}) }}" class="btn btn-small btn-info">{{ "boutton::generer" | trans }}</a> <a id="generate_access" href="{{ path("submit_developers_application_token",{ "application" : application.getId()}) }}" class="btn btn-small btn-info">{{ "boutton::generer" | trans }}</a>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div> <div class="form-actions">
<a class="btn btn-primary" href="{{ path("developers_applications") }}">{{ "boutton::retour" | trans }}</a> <a class="btn btn-primary" href="{{ path("developers_applications") }}">{{ "boutton::retour" | trans }}</a>
</div> </div>
</div> </div>

View File

@@ -15,7 +15,7 @@
{% if applications|length > 0 %} {% if applications|length > 0 %}
<ul class="app-list unstyled"> <ul class="app-list unstyled">
{% for application in applications %} {% for application in applications %}
<li id="app_{{ application.get_id() }}"> <li id="app_{{ application.getId() }}">
<a href="#appModal-{{ loop.index }}" role="button" data-toggle="modal" class="pull-right btn btn-danger btn-small" type="button"> <a href="#appModal-{{ loop.index }}" role="button" data-toggle="modal" class="pull-right btn btn-danger btn-small" type="button">
{{ "button::supprimer" | trans }} {{ "button::supprimer" | trans }}
</a> </a>
@@ -23,24 +23,24 @@
<div id="appModal-{{ loop.index }}" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div id="appModal-{{ loop.index }}" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">{{ application.get_name() }}</h3> <h3 id="myModalLabel">{{ application.getName() }}</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p>{{ "Are you sure you want to delete this application?" | trans }} </p> <p>{{ "Are you sure you want to delete this application?" | trans }} </p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">{{ "No" | trans }}</button> <button class="btn" data-dismiss="modal" aria-hidden="true">{{ "No" | trans }}</button>
<a href="{{ path("delete_developers_application", {"id" : application.get_id()}) }}" class="delete-app btn btn-info">{{ "Yes" | trans }}</a> <a href="{{ path("delete_developers_application", {"application" : application.getId()}) }}" class="delete-app btn btn-info">{{ "Yes" | trans }}</a>
</div> </div>
</div> </div>
<p class="app-row"> <p class="app-row">
<strong> <strong>
<a class="link" href="{{ path("developers_application", {"id" : application.get_id}) }}"> <a class="link" href="{{ path("developers_application", {"application" : application.getId}) }}">
{{ application.get_name() }} {{ application.getName() }}
</a> </a>
</strong> </strong>
</p> </p>
<p class="app-row">{{ application.get_description()|truncate(120, true, "...") }}</p> <p class="app-row">{{ application.getDescription()|truncate(120, true, "...") }}</p>
</li> </li>
{%endfor%} {%endfor%}
</ul> </ul>

View File

@@ -7,6 +7,7 @@ use Alchemy\Phrasea\Border\File;
use Alchemy\Phrasea\Controller\Api\V1; use Alchemy\Phrasea\Controller\Api\V1;
use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Authentication\Context; use Alchemy\Phrasea\Authentication\Context;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Model\Entities\Task; use Alchemy\Phrasea\Model\Entities\Task;
use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Model\Entities\User;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
@@ -17,37 +18,13 @@ use Symfony\Component\HttpFoundation\Response;
abstract class ApiTestCase extends \PhraseanetWebTestCase abstract class ApiTestCase extends \PhraseanetWebTestCase
{ {
/**
* @var \API_OAuth2_Token
*/
private static $token;
/**
* @var \API_OAuth2_Account
*/
private static $account;
/**
* @var \API_OAuth2_Application
*/
private static $oauthApplication;
/**
* @var \API_OAuth2_Token
*/
private static $adminToken;
/**
* @var \API_OAuth2_Account
*/
private static $adminAccount;
/**
* @var \API_OAuth2_Application
*/
private static $adminApplication;
private static $apiInitialized = false;
abstract protected function getParameters(array $parameters = []); abstract protected function getParameters(array $parameters = []);
abstract protected function unserialize($data); abstract protected function unserialize($data);
abstract protected function getAcceptMimeType(); abstract protected function getAcceptMimeType();
private $adminAccessToken;
private $userAccessToken;
public function tearDown() public function tearDown()
{ {
$this->unsetToken(); $this->unsetToken();
@@ -62,26 +39,30 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
return $this->loadApp('lib/Alchemy/Phrasea/Application/Api.php'); return $this->loadApp('lib/Alchemy/Phrasea/Application/Api.php');
}); });
if (!self::$apiInitialized) { if (null === $this->adminAccessToken) {
self::$account = \API_OAuth2_Account::load_with_user(self::$DI['app'], self::$DI['oauth2-app-user_notAdmin'], self::$DI['user_notAdmin']); $tokens = self::$DI['app']['repo.api-oauth-tokens']->findOauthTokens(self::$DI['oauth2-app-acc-user']);
self::$account->set_revoked(false); if (count($tokens) === 0) {
self::$token = self::$account->get_token()->get_value(); $this->fail(sprintf('No access token generated between user %s & application %s',
self::$DI['oauth2-app-acc-user']->getUser()->getLogin(),
self::$DI['oauth2-app-acc-user']->getApplication()->getName()
));
}
self::$adminAccount = \API_OAuth2_Account::load_with_user(self::$DI['app'], self::$DI['oauth2-app-user'], self::$DI['user']); $this->adminAccessToken = current($tokens);
self::$adminAccount->set_revoked(false);
self::$adminToken = self::$adminAccount->get_token()->get_value();
self::$apiInitialized = true;
} }
}
public static function tearDownAfterClass()
{
self::$apiInitialized = false;
self::$token = self::$account = self::$oauthApplication = self::$adminToken
= self::$adminAccount = self::$adminApplication = null;
parent::tearDownAfterClass(); if (null === $this->userAccessToken) {
$tokens = self::$DI['app']['repo.api-oauth-tokens']->findOauthTokens(self::$DI['oauth2-app-acc-user-not-admin']);
if (count($tokens) === 0) {
$this->fail(sprintf('No access token generated between user %s & application %s',
self::$DI['oauth2-app-acc-user-not-admin']->getUser()->getLogin(),
self::$DI['oauth2-app-acc-user-not-admin']->getApplication()->getName()
));
}
$this->userAccessToken = current($tokens);
}
} }
/** /**
@@ -98,7 +79,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
} }
}); });
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
self::$DI['client']->request('GET', $route, $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('GET', $route, $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
$this->assertEquals(1, $preEvent); $this->assertEquals(1, $preEvent);
@@ -107,7 +88,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testThatSessionIsClosedAfterRequest() public function testThatSessionIsClosedAfterRequest()
{ {
$this->assertCount(0, self::$DI['app']['EM']->getRepository('Phraseanet:Session')->findAll()); $this->assertCount(0, self::$DI['app']['EM']->getRepository('Phraseanet:Session')->findAll());
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
self::$DI['client']->request('GET', '/api/v1/databoxes/list/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('GET', '/api/v1/databoxes/list/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
$this->assertCount(0, self::$DI['app']['EM']->getRepository('Phraseanet:Session')->findAll()); $this->assertCount(0, self::$DI['app']['EM']->getRepository('Phraseanet:Session')->findAll());
} }
@@ -126,7 +107,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRouteNotFound() public function testRouteNotFound()
{ {
$route = '/api/v1/nothinghere'; $route = '/api/v1/nothinghere';
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
self::$DI['client']->request('GET', $route, $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('GET', $route, $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); $content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
@@ -136,7 +117,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testDataboxListRoute() public function testDataboxListRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
self::$DI['client']->request('GET', '/api/v1/databoxes/list/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('GET', '/api/v1/databoxes/list/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); $content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
@@ -167,12 +148,14 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
$fail = null; $fail = null;
try { try {
$nativeApp = self::$DI['app']['repo.api-applications']->findByClientId(\API_OAuth2_Application_Navigator::CLIENT_ID);
if (null === $nativeApp) {
throw new \Exception(sprintf('%s not found', \API_OAuth2_Application_Navigator::CLIENT_ID));
}
$account = self::$DI['app']['manipulator.api-account']->create($nativeApp, self::$DI['user']);
$token = self::$DI['app']['manipulator.api-oauth-token']->create($account);
$nativeApp = \API_OAuth2_Application::load_from_client_id(self::$DI['app'], \API_OAuth2_Application_Navigator::CLIENT_ID); $this->setToken($token->getOauthToken());
$account = \API_OAuth2_Account::create(self::$DI['app'], self::$DI['user'], $nativeApp);
$token = $account->get_token()->get_value();
$this->setToken($token);
self::$DI['client']->request('GET', '/api/v1/databoxes/list/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('GET', '/api/v1/databoxes/list/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); $content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
@@ -183,7 +166,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
$fail = $e; $fail = $e;
} }
self::$DI['app']['conf']->set(['registry', 'api-clients', 'navigator-enabled'], false); self::$DI['app']['conf']->set(['registry', 'api-clients', 'navigator-enabled'], $value);
if ($fail) { if ($fail) {
throw $fail; throw $fail;
@@ -195,7 +178,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
*/ */
public function testAdminOnlyShedulerState() public function testAdminOnlyShedulerState()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
self::$DI['client']->request('GET', '/api/v1/monitor/tasks/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('GET', '/api/v1/monitor/tasks/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); $content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
@@ -231,10 +214,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
*/ */
public function testGetMonitorTasks() public function testGetMonitorTasks()
{ {
if (null === self::$adminToken) { $this->setToken($this->adminAccessToken);
$this->markTestSkipped('there is no user with admin rights');
}
$this->setToken(self::$adminToken);
$route = '/api/v1/monitor/tasks/'; $route = '/api/v1/monitor/tasks/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -259,10 +239,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
*/ */
public function testGetScheduler() public function testGetScheduler()
{ {
if (null === self::$adminToken) { $this->setToken($this->adminAccessToken);
$this->markTestSkipped('there is no user with admin rights');
}
$this->setToken(self::$adminToken);
$route = '/api/v1/monitor/scheduler/'; $route = '/api/v1/monitor/scheduler/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -338,15 +315,11 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
{ {
$tasks = self::$DI['app']['repo.tasks']->findAll(); $tasks = self::$DI['app']['repo.tasks']->findAll();
if (null === self::$adminToken) {
$this->markTestSkipped('there is no user with admin rights');
}
if (!count($tasks)) { if (!count($tasks)) {
$this->markTestSkipped('no tasks created for the current instance'); $this->markTestSkipped('no tasks created for the current instance');
} }
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$idTask = $tasks[0]->getId(); $idTask = $tasks[0]->getId();
$route = '/api/v1/monitor/task/' . $idTask . '/'; $route = '/api/v1/monitor/task/' . $idTask . '/';
@@ -365,15 +338,11 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
{ {
$tasks = self::$DI['app']['repo.tasks']->findAll(); $tasks = self::$DI['app']['repo.tasks']->findAll();
if (null === self::$adminToken) {
$this->markTestSkipped('there is no user with admin rights');
}
if (!count($tasks)) { if (!count($tasks)) {
$this->markTestSkipped('no tasks created for the current instance'); $this->markTestSkipped('no tasks created for the current instance');
} }
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$idTask = $tasks[0]->getId(); $idTask = $tasks[0]->getId();
$route = '/api/v1/monitor/task/' . $idTask . '/'; $route = '/api/v1/monitor/task/' . $idTask . '/';
@@ -393,10 +362,10 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testUnknowGetMonitorTaskById() public function testUnknowGetMonitorTaskById()
{ {
if (null === self::$adminToken) { if (null === $this->adminAccessToken) {
$this->markTestSkipped('no tasks created for the current instance'); $this->markTestSkipped('no tasks created for the current instance');
} }
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
self::$DI['client']->followRedirects(); self::$DI['client']->followRedirects();
self::$DI['client']->request('GET', '/api/v1/monitor/task/0/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('GET', '/api/v1/monitor/task/0/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); $content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
@@ -405,17 +374,13 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testPostMonitorStartTask() public function testPostMonitorStartTask()
{ {
if (null === self::$adminToken) {
$this->markTestSkipped('there is no user with admin rights');
}
$tasks = self::$DI['app']['repo.tasks']->findAll(); $tasks = self::$DI['app']['repo.tasks']->findAll();
if (!count($tasks)) { if (!count($tasks)) {
$this->markTestSkipped('no tasks created for the current instance'); $this->markTestSkipped('no tasks created for the current instance');
} }
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$idTask = $tasks[0]->getId(); $idTask = $tasks[0]->getId();
$route = '/api/v1/monitor/task/' . $idTask . '/start/'; $route = '/api/v1/monitor/task/' . $idTask . '/start/';
@@ -437,15 +402,11 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
{ {
$tasks = self::$DI['app']['repo.tasks']->findAll(); $tasks = self::$DI['app']['repo.tasks']->findAll();
if (null === self::$adminToken) {
$this->markTestSkipped('there is no user with admin rights');
}
if (!count($tasks)) { if (!count($tasks)) {
$this->markTestSkipped('no tasks created for the current instance'); $this->markTestSkipped('no tasks created for the current instance');
} }
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$idTask = $tasks[0]->getId(); $idTask = $tasks[0]->getId();
$route = '/api/v1/monitor/task/' . $idTask . '/stop/'; $route = '/api/v1/monitor/task/' . $idTask . '/stop/';
@@ -465,12 +426,9 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testgetMonitorPhraseanet() public function testgetMonitorPhraseanet()
{ {
if (null === self::$adminToken) {
$this->markTestSkipped('there is no user with admin rights');
}
self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock(); self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock();
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
self::$DI['client']->request('GET', '/api/v1/monitor/phraseanet/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('GET', '/api/v1/monitor/phraseanet/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); $content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
@@ -488,7 +446,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRecordRoute() public function testRecordRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/'; $route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -510,7 +468,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testStoryRoute() public function testStoryRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
self::$DI['app']['session']->set('usr_id', self::$DI['user']->getId()); self::$DI['app']['session']->set('usr_id', self::$DI['user']->getId());
if (false === self::$DI['record_story_1']->hasChild(self::$DI['record_1'])) { if (false === self::$DI['record_story_1']->hasChild(self::$DI['record_1'])) {
self::$DI['record_story_1']->appendChild(self::$DI['record_1']); self::$DI['record_story_1']->appendChild(self::$DI['record_1']);
@@ -541,7 +499,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testDataboxCollectionRoute() public function testDataboxCollectionRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$databox_id = self::$DI['record_1']->get_sbas_id(); $databox_id = self::$DI['record_1']->get_sbas_id();
$route = '/api/v1/databoxes/' . $databox_id . '/collections/'; $route = '/api/v1/databoxes/' . $databox_id . '/collections/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -581,7 +539,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testDataboxStatusRoute() public function testDataboxStatusRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$databox_id = self::$DI['record_1']->get_sbas_id(); $databox_id = self::$DI['record_1']->get_sbas_id();
$databox = self::$DI['app']['phraseanet.appbox']->get_databox($databox_id); $databox = self::$DI['app']['phraseanet.appbox']->get_databox($databox_id);
$ref_status = $databox->get_statusbits(); $ref_status = $databox->get_statusbits();
@@ -630,7 +588,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testDataboxMetadatasRoute() public function testDataboxMetadatasRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$databox_id = self::$DI['record_1']->get_sbas_id(); $databox_id = self::$DI['record_1']->get_sbas_id();
$databox = self::$DI['app']['phraseanet.appbox']->get_databox($databox_id); $databox = self::$DI['app']['phraseanet.appbox']->get_databox($databox_id);
$ref_structure = $databox->get_meta_structure(); $ref_structure = $databox->get_meta_structure();
@@ -713,7 +671,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testDataboxTermsOfUseRoute() public function testDataboxTermsOfUseRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$databox_id = self::$DI['record_1']->get_sbas_id(); $databox_id = self::$DI['record_1']->get_sbas_id();
$route = '/api/v1/databoxes/' . $databox_id . '/termsOfUse/'; $route = '/api/v1/databoxes/' . $databox_id . '/termsOfUse/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -752,7 +710,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
self::$DI['app']['manipulator.user']->expects($this->once())->method('logQuery'); self::$DI['app']['manipulator.user']->expects($this->once())->method('logQuery');
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
self::$DI['client']->request('POST', '/api/v1/search/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('POST', '/api/v1/search/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); $content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
@@ -785,7 +743,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
$this->markTestSkipped('Phrasea2 extension is required for this test'); $this->markTestSkipped('Phrasea2 extension is required for this test');
} }
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
self::$DI['record_story_1']; self::$DI['record_story_1'];
@@ -821,7 +779,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
$this->markTestSkipped('Phrasea2 extension is required for this test'); $this->markTestSkipped('Phrasea2 extension is required for this test');
} }
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
self::$DI['client']->request('POST', '/api/v1/records/search/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('POST', '/api/v1/records/search/', $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); $content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
@@ -843,7 +801,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
*/ */
public function testRecordsSearchRouteWithQuery($method) public function testRecordsSearchRouteWithQuery($method)
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$searchEngine = $this->getMockBuilder('Alchemy\Phrasea\SearchEngine\SearchEngineResult') $searchEngine = $this->getMockBuilder('Alchemy\Phrasea\SearchEngine\SearchEngineResult')
->disableOriginalConstructor() ->disableOriginalConstructor()
->getMock(); ->getMock();
@@ -872,7 +830,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRecordsCaptionRoute() public function testRecordsCaptionRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock(); self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock();
$this->injectMetadatas(self::$DI['record_1']); $this->injectMetadatas(self::$DI['record_1']);
@@ -898,7 +856,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRecordsMetadatasRoute() public function testRecordsMetadatasRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/metadatas/'; $route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/metadatas/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -921,7 +879,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRecordsStatusRoute() public function testRecordsStatusRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/status/'; $route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/status/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -944,7 +902,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRecordsEmbedRoute() public function testRecordsEmbedRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/embed/'; $route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/embed/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -970,7 +928,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testStoriesEmbedRoute() public function testStoriesEmbedRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$story = self::$DI['record_story_1']; $story = self::$DI['record_story_1'];
$route = '/api/v1/stories/' . $story->get_sbas_id() . '/' . $story->get_record_id() . '/embed/'; $route = '/api/v1/stories/' . $story->get_sbas_id() . '/' . $story->get_record_id() . '/embed/';
@@ -997,7 +955,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRecordsEmbedRouteMimeType() public function testRecordsEmbedRouteMimeType()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/embed/'; $route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/embed/';
@@ -1011,7 +969,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRecordsEmbedRouteDevices() public function testRecordsEmbedRouteDevices()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/embed/'; $route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/embed/';
@@ -1023,7 +981,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRecordsRelatedRoute() public function testRecordsRelatedRoute()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/related/'; $route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/related/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -1050,7 +1008,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRecordsSetMetadatas() public function testRecordsSetMetadatas()
{ {
self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock(); self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock();
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$record = self::$DI['record_1']; $record = self::$DI['record_1'];
@@ -1108,7 +1066,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRecordsSetStatus() public function testRecordsSetStatus()
{ {
self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock(); self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock();
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/setstatus/'; $route = '/api/v1/records/' . self::$DI['record_1']->get_sbas_id() . '/' . self::$DI['record_1']->get_record_id() . '/setstatus/';
@@ -1170,7 +1128,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
$file = new File(self::$DI['app'], self::$DI['app']['mediavorus']->guess(__DIR__ . '/../../../../../files/test001.jpg'), self::$DI['collection']); $file = new File(self::$DI['app'], self::$DI['app']['mediavorus']->guess(__DIR__ . '/../../../../../files/test001.jpg'), self::$DI['collection']);
$record = \record_adapter::createFromFile($file, self::$DI['app']); $record = \record_adapter::createFromFile($file, self::$DI['app']);
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/' . $record->get_sbas_id() . '/' . $record->get_record_id() . '/setcollection/'; $route = '/api/v1/records/' . $record->get_sbas_id() . '/' . $record->get_record_id() . '/setcollection/';
@@ -1198,7 +1156,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testSearchBaskets() public function testSearchBaskets()
{ {
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$route = '/api/v1/baskets/list/'; $route = '/api/v1/baskets/list/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -1216,7 +1174,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testAddBasket() public function testAddBasket()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/baskets/add/'; $route = '/api/v1/baskets/add/';
@@ -1236,7 +1194,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testBasketContent() public function testBasketContent()
{ {
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$basketElement = self::$DI['app']['EM']->find('Phraseanet:BasketElement', 1); $basketElement = self::$DI['app']['EM']->find('Phraseanet:BasketElement', 1);
$basket = $basketElement->getBasket(); $basket = $basketElement->getBasket();
@@ -1271,7 +1229,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testSetBasketTitle() public function testSetBasketTitle()
{ {
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$basket = self::$DI['app']['EM']->find('Phraseanet:Basket', 1); $basket = self::$DI['app']['EM']->find('Phraseanet:Basket', 1);
@@ -1319,7 +1277,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testSetBasketDescription() public function testSetBasketDescription()
{ {
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$basket = self::$DI['app']['EM']->find('Phraseanet:Basket', 1); $basket = self::$DI['app']['EM']->find('Phraseanet:Basket', 1);
@@ -1342,7 +1300,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testDeleteBasket() public function testDeleteBasket()
{ {
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$route = '/api/v1/baskets/1/delete/'; $route = '/api/v1/baskets/1/delete/';
$this->evaluateMethodNotAllowedRoute($route, ['GET', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['GET', 'PUT', 'DELETE']);
@@ -1368,7 +1326,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testAddRecord() public function testAddRecord()
{ {
self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock(); self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock();
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/add/'; $route = '/api/v1/records/add/';
$params = $this->getAddRecordParameters(); $params = $this->getAddRecordParameters();
@@ -1388,7 +1346,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testAddRecordForceRecord() public function testAddRecordForceRecord()
{ {
self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock(); self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock();
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/add/'; $route = '/api/v1/records/add/';
$params = $this->getAddRecordParameters(); $params = $this->getAddRecordParameters();
@@ -1413,7 +1371,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testAddRecordForceLazaret() public function testAddRecordForceLazaret()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/add/'; $route = '/api/v1/records/add/';
$params = $this->getAddRecordParameters(); $params = $this->getAddRecordParameters();
@@ -1437,7 +1395,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testAddRecordWrongBehavior() public function testAddRecordWrongBehavior()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/add/'; $route = '/api/v1/records/add/';
$params = $this->getAddRecordParameters(); $params = $this->getAddRecordParameters();
@@ -1452,7 +1410,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testAddRecordWrongBaseId() public function testAddRecordWrongBaseId()
{ {
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$route = '/api/v1/records/add/'; $route = '/api/v1/records/add/';
$params = $this->getAddRecordParameters(); $params = $this->getAddRecordParameters();
@@ -1467,7 +1425,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testAddRecordNoBaseId() public function testAddRecordNoBaseId()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/add/'; $route = '/api/v1/records/add/';
$params = $this->getAddRecordParameters(); $params = $this->getAddRecordParameters();
@@ -1482,7 +1440,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testAddRecordMultipleFiles() public function testAddRecordMultipleFiles()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/add/'; $route = '/api/v1/records/add/';
$file = [ $file = [
@@ -1499,7 +1457,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testAddRecordNofile() public function testAddRecordNofile()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/records/add/'; $route = '/api/v1/records/add/';
self::$DI['client']->request('POST', $route, $this->getParameters($this->getAddRecordParameters()), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); self::$DI['client']->request('POST', $route, $this->getParameters($this->getAddRecordParameters()), [], ['HTTP_Accept' => $this->getAcceptMimeType()]);
@@ -1513,7 +1471,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
{ {
$created_feed = self::$DI['app']['EM']->find('Phraseanet:Feed', 1); $created_feed = self::$DI['app']['EM']->find('Phraseanet:Feed', 1);
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/feeds/list/'; $route = '/api/v1/feeds/list/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -1564,7 +1522,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
self::$DI['app']['EM']->persist($created_entry); self::$DI['app']['EM']->persist($created_entry);
self::$DI['app']['EM']->flush(); self::$DI['app']['EM']->flush();
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/feeds/content/'; $route = '/api/v1/feeds/content/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -1609,7 +1567,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
$feed = self::$DI['app']['EM']->find('Phraseanet:Feed', 1); $feed = self::$DI['app']['EM']->find('Phraseanet:Feed', 1);
$created_entry = $feed->getEntries()->first(); $created_entry = $feed->getEntries()->first();
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/feeds/entry/' . $created_entry->getId() . '/'; $route = '/api/v1/feeds/entry/' . $created_entry->getId() . '/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -1638,7 +1596,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
$created_feed->setCollection(self::$DI['collection_no_access']); $created_feed->setCollection(self::$DI['collection_no_access']);
$this->setToken(self::$adminToken); $this->setToken($this->adminAccessToken);
$route = '/api/v1/feeds/entry/' . $created_entry->getId() . '/'; $route = '/api/v1/feeds/entry/' . $created_entry->getId() . '/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -1666,7 +1624,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
self::$DI['app']['EM']->persist($created_entry); self::$DI['app']['EM']->persist($created_entry);
self::$DI['app']['EM']->flush(); self::$DI['app']['EM']->flush();
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/feeds/' . $created_feed->getId() . '/content/'; $route = '/api/v1/feeds/' . $created_feed->getId() . '/content/';
$this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']); $this->evaluateMethodNotAllowedRoute($route, ['POST', 'PUT', 'DELETE']);
@@ -1702,7 +1660,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testQuarantineList() public function testQuarantineList()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/quarantine/list/'; $route = '/api/v1/quarantine/list/';
$quarantineItemId = self::$DI['lazaret_1']->getId(); $quarantineItemId = self::$DI['lazaret_1']->getId();
@@ -1733,7 +1691,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testQuarantineContent() public function testQuarantineContent()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$quarantineItemId = self::$DI['lazaret_1']->getId(); $quarantineItemId = self::$DI['lazaret_1']->getId();
$route = '/api/v1/quarantine/item/' . $quarantineItemId . '/'; $route = '/api/v1/quarantine/item/' . $quarantineItemId . '/';
@@ -1778,7 +1736,7 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase
public function testRouteMe() public function testRouteMe()
{ {
$this->setToken(self::$token); $this->setToken($this->userAccessToken);
$route = '/api/v1/me/'; $route = '/api/v1/me/';

View File

@@ -4,21 +4,10 @@ namespace Alchemy\Tests\Phrasea\Controller\Api;
use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Authentication\Context; use Alchemy\Phrasea\Authentication\Context;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
/**
* Test oauthv2 flow based on ietf authv2 spec
* @link http://tools.ietf.org/html/draft-ietf-oauth-v2-18
*/
class OAuth2Test extends \PhraseanetAuthenticatedWebTestCase class OAuth2Test extends \PhraseanetAuthenticatedWebTestCase
{ {
/**
*
* @var API_OAuth2_Application
*/
public static $account_id;
public static $account;
public $oauth;
protected $client;
protected $queryParameters; protected $queryParameters;
public function setUp() public function setUp()
@@ -31,48 +20,23 @@ class OAuth2Test extends \PhraseanetAuthenticatedWebTestCase
$this->queryParameters = [ $this->queryParameters = [
"response_type" => "code", "response_type" => "code",
"client_id" => self::$DI['oauth2-app-user']->get_client_id(), "client_id" => self::$DI['oauth2-app-user']->getClientId(),
"redirect_uri" => self::$DI['oauth2-app-user']->get_redirect_uri(), "redirect_uri" => self::$DI['oauth2-app-user']->getRedirectUri(),
"scope" => "", "scope" => "",
"state" => "valueTest" "state" => "valueTest"
]; ];
} }
public static function tearDownAfterClass()
{
self::$account_id = self::$account = null;
parent::tearDownAfterClass();
}
public static function deleteInsertedRow(\appbox $appbox, \API_OAuth2_Application $app)
{
$conn = $appbox->get_connection();
$sql = '
DELETE FROM api_applications
WHERE application_id = :id
';
$t = [':id' => $app->get_id()];
$stmt = $conn->prepare($sql);
$stmt->execute($t);
$stmt->closeCursor();
$sql = '
DELETE FROM api_accounts
WHERE api_account_id = :id
';
$acc = self::getAccount();
$t = [':id' => $acc->get_id()];
$stmt = $conn->prepare($sql);
$stmt->execute($t);
$stmt->closeCursor();
}
/** /**
* @dataProvider provideEventNames * @dataProvider provideEventNames
*/ */
public function testThatEventsAreTriggered($revoked, $method, $eventName, $className) public function testThatEventsAreTriggered($revoked, $method, $eventName, $className)
{ {
$acc = self::getAccount(); if ($revoked) {
$acc->set_revoked($revoked); // revoked to show form self::$DI['app']['manipulator.api-account']->revokeAccess(self::$DI['oauth2-app-acc-user']);
} else {
self::$DI['app']['manipulator.api-account']->authorizeAccess(self::$DI['oauth2-app-acc-user']);
}
$preEvent = 0; $preEvent = 0;
self::$DI['app']['dispatcher']->addListener($eventName, function ($event) use (&$preEvent, $className) { self::$DI['app']['dispatcher']->addListener($eventName, function ($event) use (&$preEvent, $className) {
@@ -96,32 +60,6 @@ class OAuth2Test extends \PhraseanetAuthenticatedWebTestCase
]; ];
} }
public static function getApp($rowId)
{
$sql = "SELECT * FROM api_applications WHERE application_id = :app_id";
$t = [":app_id" => $rowId];
$conn = self::$DI['app']['phraseanet.appbox']->get_connection();
$stmt = $conn->prepare($sql);
$stmt->execute($t);
$result = $stmt->fetch(\PDO::FETCH_ASSOC);
$stmt->closeCursor();
return $result;
}
public static function getAccount()
{
$sql = "SELECT api_account_id FROM api_accounts WHERE application_id = :app_id AND usr_id = :usr_id";
$t = [":app_id" => self::$DI['oauth2-app-user']->get_id(), ":usr_id" => self::$DI['user']->getId()];
$conn = self::$DI['app']['phraseanet.appbox']->get_connection();
$stmt = $conn->prepare($sql);
$stmt->execute($t);
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
$stmt->closeCursor();
return new \API_OAuth2_Account(self::$DI['app'], $row["api_account_id"]);
}
public function setQueryParameters($parameter, $value) public function setQueryParameters($parameter, $value)
{ {
$this->queryParameters[$parameter] = $value; $this->queryParameters[$parameter] = $value;
@@ -129,18 +67,17 @@ class OAuth2Test extends \PhraseanetAuthenticatedWebTestCase
public function unsetQueryParameter($parameter) public function unsetQueryParameter($parameter)
{ {
if (isset($this->queryParameters[$parameter])) if (isset($this->queryParameters[$parameter])) {
unset($this->queryParameters[$parameter]); unset($this->queryParameters[$parameter]);
}
} }
public function testAuthorizeRedirect() public function testAuthorizeRedirect()
{ {
//session off //session off
$apps = \API_OAuth2_Application::load_authorized_app_by_user(self::$DI['app'], self::$DI['user']); $apps = self::$DI['app']['repo.api-applications']->findAuthorizedAppsByUser(self::$DI['user']);
foreach ($apps as $app) { foreach ($apps as $app) {
if ($app->get_client_id() == self::$DI['oauth2-app-user']->get_client_id()) { if ($app->getClientId() === self::$DI['oauth2-app-user']->getClientId()) {
$authorize = true;
self::$DI['client']->followRedirects(); self::$DI['client']->followRedirects();
} }
} }
@@ -148,13 +85,11 @@ class OAuth2Test extends \PhraseanetAuthenticatedWebTestCase
public function testAuthorize() public function testAuthorize()
{ {
$acc = self::getAccount(); self::$DI['app']['manipulator.api-account']->revokeAccess(self::$DI['oauth2-app-acc-user']);
$acc->set_revoked(true); // revoked to show form
self::$DI['client']->request('GET', '/api/oauthv2/authorize', $this->queryParameters); self::$DI['client']->request('GET', '/api/oauthv2/authorize', $this->queryParameters);
$this->assertTrue(self::$DI['client']->getResponse()->isSuccessful()); $this->assertTrue(self::$DI['client']->getResponse()->isSuccessful());
$this->assertRegExp("/" . self::$DI['oauth2-app-user']->get_client_id() . "/", self::$DI['client']->getResponse()->getContent()); $this->assertRegExp("/" . self::$DI['oauth2-app-user']->getCLientId() . "/", self::$DI['client']->getResponse()->getContent());
$this->assertRegExp("/" . str_replace("/", '\/', self::$DI['oauth2-app-user']->get_redirect_uri()) . "/", self::$DI['client']->getResponse()->getContent()); $this->assertRegExp("/" . str_replace("/", '\/', self::$DI['oauth2-app-user']->getRedirectUri()) . "/", self::$DI['client']->getResponse()->getContent());
$this->assertRegExp("/" . $this->queryParameters["response_type"] . "/", self::$DI['client']->getResponse()->getContent()); $this->assertRegExp("/" . $this->queryParameters["response_type"] . "/", self::$DI['client']->getResponse()->getContent());
$this->assertRegExp("/" . $this->queryParameters["scope"] . "/", self::$DI['client']->getResponse()->getContent()); $this->assertRegExp("/" . $this->queryParameters["scope"] . "/", self::$DI['client']->getResponse()->getContent());
$this->assertRegExp("/" . $this->queryParameters["state"] . "/", self::$DI['client']->getResponse()->getContent()); $this->assertRegExp("/" . $this->queryParameters["state"] . "/", self::$DI['client']->getResponse()->getContent());
@@ -165,7 +100,6 @@ class OAuth2Test extends \PhraseanetAuthenticatedWebTestCase
$this->setQueryParameters('grant_type', 'authorization_code'); $this->setQueryParameters('grant_type', 'authorization_code');
$this->setQueryParameters('code', '12345678918'); $this->setQueryParameters('code', '12345678918');
self::$DI['client']->request('POST', '/api/oauthv2/token', $this->queryParameters); self::$DI['client']->request('POST', '/api/oauthv2/token', $this->queryParameters);
$this->assertEquals(400, self::$DI['client']->getResponse()->getStatusCode()); $this->assertEquals(400, self::$DI['client']->getResponse()->getStatusCode());
} }
} }

View File

@@ -355,7 +355,6 @@ class AccountTest extends \PhraseanetAuthenticatedWebTestCase
public function testAUthorizedAppGrantAccessBadRequest() public function testAUthorizedAppGrantAccessBadRequest()
{ {
self::$DI['client']->request('GET', '/account/security/application/3/grant/'); self::$DI['client']->request('GET', '/account/security/application/3/grant/');
$this->assertBadResponse(self::$DI['client']->getResponse()); $this->assertBadResponse(self::$DI['client']->getResponse());
} }
@@ -376,7 +375,7 @@ class AccountTest extends \PhraseanetAuthenticatedWebTestCase
*/ */
public function testAUthorizedAppGrantAccessSuccessfull($revoke, $expected) public function testAUthorizedAppGrantAccessSuccessfull($revoke, $expected)
{ {
self::$DI['client']->request('GET', '/account/security/application/' . self::$DI['oauth2-app-user']->get_id() . '/grant/', [ self::$DI['client']->request('GET', '/account/security/application/' . self::$DI['oauth2-app-user']->getId() . '/grant/', [
'revoke' => $revoke 'revoke' => $revoke
], [], [ ], [], [
'HTTP_ACCEPT' => 'application/json', 'HTTP_ACCEPT' => 'application/json',
@@ -384,20 +383,15 @@ class AccountTest extends \PhraseanetAuthenticatedWebTestCase
]); ]);
$response = self::$DI['client']->getResponse(); $response = self::$DI['client']->getResponse();
$this->assertTrue($response->isOk()); $this->assertTrue($response->isOk());
$json = json_decode($response->getContent()); $json = json_decode($response->getContent());
$this->assertInstanceOf('StdClass', $json); $this->assertInstanceOf('StdClass', $json);
$this->assertObjectHasAttribute('success', $json); $this->assertObjectHasAttribute('success', $json);
$this->assertTrue($json->success); $this->assertTrue($json->success);
$account = \API_OAuth2_Account::load_with_user( $account = self::$DI['app']['repo.api-accounts']->findByUserAndApplication(self::$DI['user'], self::$DI['oauth2-app-user']);
self::$DI['app']
, self::$DI['oauth2-app-user']
, self::$DI['user']
);
$this->assertEquals($expected, $account->is_revoked()); $this->assertEquals($expected, $account->isRevoked());
} }
public function revokeProvider() public function revokeProvider()

View File

@@ -2,6 +2,7 @@
namespace Alchemy\Tests\Phrasea\Controller\Root; namespace Alchemy\Tests\Phrasea\Controller\Root;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
@@ -34,7 +35,7 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
public function testPostNewAppInvalidArguments() public function testPostNewAppInvalidArguments()
{ {
$crawler = self::$DI['client']->request('POST', '/developers/application/', [ $crawler = self::$DI['client']->request('POST', '/developers/application/', [
'type' => \API_OAuth2_Application::WEB_TYPE, 'type' => ApiApplication::WEB_TYPE,
'name' => '', 'name' => '',
'description' => 'okok', 'description' => 'okok',
'website' => 'my.website.com', 'website' => 'my.website.com',
@@ -55,11 +56,11 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
*/ */
public function testPostNewApp() public function testPostNewApp()
{ {
$apps = \API_OAuth2_Application::load_dev_app_by_user(self::$DI['app'], self::$DI['user']); $apps = self::$DI['app']['repo.api-applications']->findByCreator(self::$DI['user']);
$nbApp = count($apps); $nbApp = count($apps);
self::$DI['client']->request('POST', '/developers/application/', [ self::$DI['client']->request('POST', '/developers/application/', [
'type' => \API_OAuth2_Application::WEB_TYPE, 'type' => ApiApplication::WEB_TYPE,
'name' => 'hello', 'name' => 'hello',
'description' => 'okok', 'description' => 'okok',
'website' => 'my.website.com', 'website' => 'my.website.com',
@@ -68,7 +69,7 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
'scheme-callback' => 'http://' 'scheme-callback' => 'http://'
]); ]);
$apps = \API_OAuth2_Application::load_dev_app_by_user(self::$DI['app'], self::$DI['user']); $apps = self::$DI['app']['repo.api-applications']->findByCreator(self::$DI['user']);
$this->assertTrue(self::$DI['client']->getResponse()->isRedirect()); $this->assertTrue(self::$DI['client']->getResponse()->isRedirect());
$this->assertGreaterThan($nbApp, count($apps)); $this->assertGreaterThan($nbApp, count($apps));
@@ -77,7 +78,7 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
/** /**
* @cover \Alchemy\Phrasea\Controller\Root\Developers::getApp * @cover \Alchemy\Phrasea\Controller\Root\Developers::getApp
*/ */
public function testGetUnknowApp() public function testGetUnknownApp()
{ {
self::$DI['client']->request('GET', '/developers/application/0/'); self::$DI['client']->request('GET', '/developers/application/0/');
@@ -90,7 +91,7 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
public function testGetApp() public function testGetApp()
{ {
$oauthApp = self::$DI['oauth2-app-user']; $oauthApp = self::$DI['oauth2-app-user'];
self::$DI['client']->request('GET', '/developers/application/' . $oauthApp->get_id() . '/'); self::$DI['client']->request('GET', '/developers/application/' . $oauthApp->getId() . '/');
$this->assertTrue(self::$DI['client']->getResponse()->isOk()); $this->assertTrue(self::$DI['client']->getResponse()->isOk());
} }
@@ -121,16 +122,17 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
*/ */
public function testDeleteApp() public function testDeleteApp()
{ {
$oauthApp = \API_OAuth2_Application::create(self::$DI['app'], self::$DI['user'], 'test app'); $oauthApp = self::$DI['app']['manipulator.api-application']->create(
$this->XMLHTTPRequest('DELETE', '/developers/application/' . $oauthApp->get_id() . '/'); 'test app',
ApiApplication::DESKTOP_TYPE,
'',
'http://phraseanet.com/'
);
$id = $oauthApp->getId();
$this->XMLHTTPRequest('DELETE', '/developers/application/' . $id . '/');
$this->assertTrue(self::$DI['client']->getResponse()->isOk()); $this->assertTrue(self::$DI['client']->getResponse()->isOk());
try { $this->assertNull(self::$DI['app']['repo.api-applications']->find($id));
new \API_OAuth2_Application(self::$DI['app'], $oauthApp->get_id());
$this->fail('Application not deleted');
} catch (NotFoundHttpException $e) {
}
} }
/** /**
@@ -163,7 +165,7 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
public function testRenewAppCallbackError2() public function testRenewAppCallbackError2()
{ {
$oauthApp = self::$DI['oauth2-app-user']; $oauthApp = self::$DI['oauth2-app-user'];
$this->XMLHTTPRequest('POST', '/developers/application/'.$oauthApp->get_id().'/callback/'); $this->XMLHTTPRequest('POST', '/developers/application/'.$oauthApp->getId().'/callback/');
$this->assertTrue(self::$DI['client']->getResponse()->isOk()); $this->assertTrue(self::$DI['client']->getResponse()->isOk());
$content = json_decode(self::$DI['client']->getResponse()->getContent()); $content = json_decode(self::$DI['client']->getResponse()->getContent());
$this->assertFalse($content->success); $this->assertFalse($content->success);
@@ -176,15 +178,15 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
{ {
$oauthApp = self::$DI['oauth2-app-user']; $oauthApp = self::$DI['oauth2-app-user'];
$this->XMLHTTPRequest('POST', '/developers/application/' . $oauthApp->get_id() . '/callback/', [ $this->XMLHTTPRequest('POST', '/developers/application/' . $oauthApp->getId() . '/callback/', [
'callback' => 'my.callback.com' 'callback' => 'http://my.callback.com'
]); ]);
$this->assertTrue(self::$DI['client']->getResponse()->isOk()); $this->assertTrue(self::$DI['client']->getResponse()->isOk());
$content = json_decode(self::$DI['client']->getResponse()->getContent()); $content = json_decode(self::$DI['client']->getResponse()->getContent());
$this->assertTrue($content->success); $this->assertTrue($content->success);
$oauthApp = new \API_OAuth2_Application(self::$DI['app'], $oauthApp->get_id()); $oauthApp = self::$DI['app']['repo.api-applications']->find($oauthApp->getId());
$this->assertEquals('my.callback.com', $oauthApp->get_redirect_uri()); $this->assertEquals('http://my.callback.com', $oauthApp->getRedirectUri());
} }
/** /**
@@ -204,12 +206,11 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
{ {
$this->XMLHTTPRequest('POST', '/developers/application/0/access_token/', [ $this->XMLHTTPRequest('POST', '/developers/application/0/access_token/', [
'callback' => 'my.callback.com' 'callback' => 'my.callback.com'
]); ]);
$this->assertTrue(self::$DI['client']->getResponse()->isOk()); $this->assertTrue(self::$DI['client']->getResponse()->isOk());
$content = json_decode(self::$DI['client']->getResponse()->getContent()); $content = json_decode(self::$DI['client']->getResponse()->getContent());
$this->assertFalse($content->success); $this->assertFalse($content->success);
$this->assertNull($content->token);
} }
/** /**
@@ -219,7 +220,7 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
{ {
$oauthApp = self::$DI['oauth2-app-user']; $oauthApp = self::$DI['oauth2-app-user'];
$this->XMLHTTPRequest('POST', '/developers/application/' . $oauthApp->get_id() . '/access_token/'); $this->XMLHTTPRequest('POST', '/developers/application/' . $oauthApp->getId() . '/access_token/');
$this->assertTrue(self::$DI['client']->getResponse()->isOk()); $this->assertTrue(self::$DI['client']->getResponse()->isOk());
$content = json_decode(self::$DI['client']->getResponse()->getContent()); $content = json_decode(self::$DI['client']->getResponse()->getContent());
@@ -258,14 +259,14 @@ class DevelopersTest extends \PhraseanetAuthenticatedWebTestCase
{ {
$oauthApp = self::$DI['oauth2-app-user']; $oauthApp = self::$DI['oauth2-app-user'];
$this->XMLHTTPRequest('POST', '/developers/application/' . $oauthApp->get_id() . '/authorize_grant_password/', [ $this->XMLHTTPRequest('POST', '/developers/application/' . $oauthApp->getId() . '/authorize_grant_password/', [
'grant' => '1' 'grant' => '1'
]); ]);
$this->assertTrue(self::$DI['client']->getResponse()->isOk()); $this->assertTrue(self::$DI['client']->getResponse()->isOk());
$content = json_decode(self::$DI['client']->getResponse()->getContent()); $content = json_decode(self::$DI['client']->getResponse()->getContent());
$this->assertTrue($content->success); $this->assertTrue($content->success);
$oauthApp = new \API_OAuth2_Application(self::$DI['app'], $oauthApp->get_id()); $oauthApp = self::$DI['app']['repo.api-applications']->find($oauthApp->getId());
$this->assertTrue($oauthApp->is_password_granted()); $this->assertTrue($oauthApp->isPasswordGranted());
} }
} }

View File

@@ -2,7 +2,7 @@
namespace Alchemy\Tests\Phrasea\Core\Provider; namespace Alchemy\Tests\Phrasea\Core\Provider;
class ConvertersServiceProvider extends ServiceProviderTestCase class ConvertersServiceProviderTest extends ServiceProviderTestCase
{ {
public function provideServiceDescription() public function provideServiceDescription()
{ {
@@ -10,17 +10,22 @@ class ConvertersServiceProvider extends ServiceProviderTestCase
[ [
'Alchemy\Phrasea\Core\Provider\ConvertersServiceProvider', 'Alchemy\Phrasea\Core\Provider\ConvertersServiceProvider',
'converter.task', 'converter.task',
'Alchemy\Phrasea\Controller\Converter\TaskConverter' 'Alchemy\Phrasea\Model\Converter\TaskConverter'
], ],
[ [
'Alchemy\Phrasea\Core\Provider\ConvertersServiceProvider', 'Alchemy\Phrasea\Core\Provider\ConvertersServiceProvider',
'converter.basket', 'converter.basket',
'Alchemy\Phrasea\Controller\Converter\BasketConverter' 'Alchemy\Phrasea\Model\Converter\BasketConverter'
], ],
[ [
'Alchemy\Phrasea\Core\Provider\ConvertersServiceProvider', 'Alchemy\Phrasea\Core\Provider\ConvertersServiceProvider',
'converter.token', 'converter.token',
'Alchemy\Phrasea\Controller\Converter\TokenConverter' 'Alchemy\Phrasea\Model\Converter\TokenConverter'
],
[
'Alchemy\Phrasea\Core\Provider\ConvertersServiceProvider',
'converter.api-application',
'Alchemy\Phrasea\Model\Converter\ApiApplicationConverter'
], ],
]; ];
} }

View File

@@ -27,6 +27,36 @@ class ManipulatorServiceProviderTest extends ServiceProviderTestCase
'manipulator.token', 'manipulator.token',
'Alchemy\Phrasea\Model\Manipulator\TokenManipulator' 'Alchemy\Phrasea\Model\Manipulator\TokenManipulator'
], ],
[
'Alchemy\Phrasea\Core\Provider\ManipulatorServiceProvider',
'manipulator.api-application',
'Alchemy\Phrasea\Model\Manipulator\ApiApplicationManipulator'
],
[
'Alchemy\Phrasea\Core\Provider\ManipulatorServiceProvider',
'manipulator.api-account',
'Alchemy\Phrasea\Model\Manipulator\ApiAccountManipulator'
],
[
'Alchemy\Phrasea\Core\Provider\ManipulatorServiceProvider',
'manipulator.api-log',
'Alchemy\Phrasea\Model\Manipulator\ApiLogManipulator'
],
[
'Alchemy\Phrasea\Core\Provider\ManipulatorServiceProvider',
'manipulator.api-oauth-token',
'Alchemy\Phrasea\Model\Manipulator\ApiOauthTokenManipulator'
],
[
'Alchemy\Phrasea\Core\Provider\ManipulatorServiceProvider',
'manipulator.api-oauth-code',
'Alchemy\Phrasea\Model\Manipulator\ApiOauthCodeManipulator'
],
[
'Alchemy\Phrasea\Core\Provider\ManipulatorServiceProvider',
'manipulator.api-oauth-refresh-token',
'Alchemy\Phrasea\Model\Manipulator\ApiOauthRefreshTokenManipulator'
],
]; ];
} }
} }

View File

@@ -33,6 +33,12 @@ class RepositoriesServiceProviderTest extends ServiceProviderTestCase
['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.user-queries', 'Alchemy\Phrasea\Model\Repositories\UserQueryRepository'], ['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.user-queries', 'Alchemy\Phrasea\Model\Repositories\UserQueryRepository'],
['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.tokens', 'Alchemy\Phrasea\Model\Repositories\TokenRepository'], ['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.tokens', 'Alchemy\Phrasea\Model\Repositories\TokenRepository'],
['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.presets', 'Alchemy\Phrasea\Model\Repositories\PresetRepository'], ['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.presets', 'Alchemy\Phrasea\Model\Repositories\PresetRepository'],
['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.api-applications', 'Alchemy\Phrasea\Model\Repositories\ApiApplicationRepository'],
['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.api-accounts', 'Alchemy\Phrasea\Model\Repositories\ApiAccountRepository'],
['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.api-logs', 'Alchemy\Phrasea\Model\Repositories\ApiLogRepository'],
['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.api-oauth-tokens', 'Alchemy\Phrasea\Model\Repositories\ApiOauthTokenRepository'],
['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.api-oauth-codes', 'Alchemy\Phrasea\Model\Repositories\ApiOauthCodeRepository'],
['Alchemy\Phrasea\Core\Provider\RepositoriesServiceProvider', 'repo.api-oauth-refresh-tokens', 'Alchemy\Phrasea\Model\Repositories\ApiOauthRefreshTokenRepository'],
]; ];
} }
} }

View File

@@ -0,0 +1,60 @@
<?php
namespace Alchemy\Tests\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Controller\Api\V1;
use Alchemy\Phrasea\Model\Manipulator\ApiAccountManipulator;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
class ApiAccountManipulatorTest extends \PhraseanetTestCase
{
public function testCreate()
{
$manipulator = new ApiAccountManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-accounts']);
$nbApps = count(self::$DI['app']['repo.api-accounts']->findAll());
$account = $manipulator->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$this->assertGreaterThan($nbApps, count(self::$DI['app']['repo.api-accounts']->findAll()));
$this->assertFalse($account->isRevoked());
$this->assertEquals(V1::VERSION, $account->getApiVersion());
$this->assertGreaterThan($nbApps, count(self::$DI['app']['repo.api-accounts']->findAll()));
}
public function testDelete()
{
$manipulator = new ApiAccountManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-accounts']);
$account = $manipulator->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$accountMem = clone $account;
$countBefore = count(self::$DI['app']['repo.api-accounts']->findAll());
self::$DI['app']['manipulator.api-oauth-token']->create($account);
$manipulator->delete($account);
$this->assertGreaterThan(count(self::$DI['app']['repo.api-accounts']->findAll()), $countBefore);
$tokens = self::$DI['app']['repo.api-oauth-tokens']->findOauthTokens($accountMem);
$this->assertEquals(0, count($tokens));
}
public function testUpdate()
{
$manipulator = new ApiAccountManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-accounts']);
$account = $manipulator->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$account->setApiVersion(24);
$manipulator->update($account);
$account = self::$DI['app']['repo.api-accounts']->find($account->getId());
$this->assertEquals(24, $account->getApiVersion());
}
public function testAuthorizeAccess()
{
$manipulator = new ApiAccountManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-accounts']);
$account = $manipulator->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$manipulator->authorizeAccess($account);
$this->assertFalse($account->isRevoked());
}
public function testRevokeAccess()
{
$manipulator = new ApiAccountManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-accounts']);
$account = $manipulator->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$manipulator->revokeAccess($account);
$this->assertTrue($account->isRevoked());
}
}

View File

@@ -0,0 +1,153 @@
<?php
namespace Alchemy\Tests\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Manipulator\ApiApplicationManipulator;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
class ApiApplicationManipulatorTest extends \PhraseanetTestCase
{
public function testCreateDesktopApplication()
{
$manipulator = new ApiApplicationManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-applications'], self::$DI['app']['random.medium']);
$nbApps = count(self::$DI['app']['repo.api-applications']->findAll());
$application = $manipulator->create(
'desktop-app',
ApiApplication::DESKTOP_TYPE,
'Desktop application description',
'http://desktop-app-url.net'
);
$this->assertGreaterThan($nbApps, count(self::$DI['app']['repo.api-applications']->findAll()));
$this->assertNotNull($application->getClientId());
$this->assertNotNull($application->getClientSecret());
$this->assertNotNull($application->getNonce());
$this->assertEquals('desktop-app', $application->getName());
$this->assertEquals(ApiApplication::DESKTOP_TYPE, $application->getType());
$this->assertEquals('http://desktop-app-url.net', $application->getWebsite());
$this->assertEquals(ApiApplication::NATIVE_APP_REDIRECT_URI, $application->getRedirectUri());
}
public function testCreateWebApplication()
{
$manipulator = new ApiApplicationManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-applications'], self::$DI['app']['random.medium']);
$nbApps = count(self::$DI['app']['repo.api-applications']->findAll());
$application = $manipulator->create(
'web-app',
ApiApplication::WEB_TYPE,
'Desktop application description',
'http://web-app-url.net',
self::$DI['user'],
'http://web-app-url.net/callback'
);
$this->assertGreaterThan($nbApps, count(self::$DI['app']['repo.api-applications']->findAll()));
$this->assertNotNull($application->getClientId());
$this->assertNotNull($application->getClientSecret());
$this->assertNotNull($application->getNonce());
$this->assertEquals('web-app', $application->getName());
$this->assertEquals(ApiApplication::WEB_TYPE, $application->getType());
$this->assertEquals('http://web-app-url.net', $application->getWebsite());
$this->assertEquals('http://web-app-url.net/callback', $application->getRedirectUri());
}
public function testDelete()
{
$manipulator = new ApiApplicationManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-applications'], self::$DI['app']['random.medium']);
$application = $manipulator->create(
'desktop-app',
ApiApplication::DESKTOP_TYPE,
'Desktop application description',
'http://desktop-app-url.net'
);
$applicationSave = clone $application;
$countBefore = count(self::$DI['app']['repo.api-applications']->findAll());
$account = self::$DI['app']['manipulator.api-account']->create($application, self::$DI['user']);
$accountMem = clone $account;
self::$DI['app']['manipulator.api-oauth-token']->create($account);
$manipulator->delete($application);
$this->assertGreaterThan(count(self::$DI['app']['repo.api-applications']->findAll()), $countBefore);
$accounts = self::$DI['app']['repo.api-accounts']->findByUserAndApplication(self::$DI['user'], $applicationSave);
$this->assertEquals(0, count($accounts));
$tokens = self::$DI['app']['repo.api-oauth-tokens']->findOauthTokens($accountMem);
$this->assertEquals(0, count($tokens));
}
public function testUpdate()
{
$manipulator = new ApiApplicationManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-applications'], self::$DI['app']['random.medium']);
$application = $manipulator->create(
'desktop-app',
ApiApplication::DESKTOP_TYPE,
'Desktop application description',
'http://desktop-app-url.net'
);
$application->setName('new-desktop-app');
$manipulator->update($application);
$application = self::$DI['app']['repo.api-applications']->find($application->getId());
$this->assertEquals('new-desktop-app', $application->getName());
}
public function testSetType()
{
$manipulator = new ApiApplicationManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-applications'], self::$DI['app']['random.medium']);
$application = $manipulator->create(
'desktop-app',
ApiApplication::DESKTOP_TYPE,
'Desktop application description',
'http://desktop-app-url.net'
);
try {
$manipulator->setType($application, 'invalid-type');
$this->fail('Invalid argument exception should be raised');
} catch (InvalidArgumentException $e) {
}
}
public function testSetRedirectUri()
{
$manipulator = new ApiApplicationManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-applications'], self::$DI['app']['random.medium']);
$application = $manipulator->create(
'desktop-app',
ApiApplication::DESKTOP_TYPE,
'Desktop application description',
'http://desktop-app-url.net'
);
$manipulator->setRedirectUri($application, 'invalid-url.com');
$this->assertEquals(ApiApplication::NATIVE_APP_REDIRECT_URI, $application->getRedirectUri());
$application = $manipulator->create(
'web-app',
ApiApplication::WEB_TYPE,
'Desktop application description',
'http://web-app-url.net',
self::$DI['user'],
'http://web-app-url.net/callback'
);
try {
$manipulator->setWebsiteUrl($application, 'invalid-url.com');
$this->fail('Invalid argument exception should be raised');
} catch (InvalidArgumentException $e) {
}
}
public function testSetWebsiteUrl()
{
$manipulator = new ApiApplicationManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-applications'], self::$DI['app']['random.medium']);
$application = $manipulator->create(
'desktop-app',
ApiApplication::DESKTOP_TYPE,
'Desktop application description',
'http://desktop-app-url.net'
);
try {
$manipulator->setWebsiteUrl($application, 'invalid-url.com');
$this->fail('Invalid argument exception should be raised');
} catch (InvalidArgumentException $e) {
}
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Alchemy\Tests\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Model\Manipulator\ApiLogManipulator;
use Alchemy\Phrasea\Model\Manipulator\ApiAccountManipulator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ApiLogManipulatorTest extends \PhraseanetTestCase
{
public function testCreate()
{
$manipulator = new ApiAccountManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-accounts']);
$account = $manipulator->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$nbLogs = count(self::$DI['app']['repo.api-logs']->findAll());
$manipulator = new ApiLogManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-logs']);
$manipulator->create($account, Request::create('/databoxes/list/', 'POST'), new Response());
$this->assertGreaterThan($nbLogs, count(self::$DI['app']['repo.api-accounts']->findAll()));
}
/**
* @dataProvider apiRouteProvider
*/
public function testsLogHydration($path, $expected)
{
$emMock = $this->getMock('\Doctrine\ORM\EntityManager', array('persist', 'flush'), array(), '', false);
$account = $this->getMock('\Alchemy\Phrasea\Model\Entities\ApiAccount');
$manipulator = new ApiLogManipulator($emMock, self::$DI['app']['repo.api-logs']);
$log = $manipulator->create($account, Request::create($path, 'POST'), new Response());
$this->assertEquals($expected['resource'], $log->getResource());
$this->assertEquals($expected['general'], $log->getGeneral());
$this->assertEquals($expected['aspect'], $log->getAspect());
$this->assertEquals($expected['action'], $log->getAction());
}
public function testDelete()
{
$manipulator = new ApiAccountManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-accounts']);
$account = $manipulator->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$manipulator = new ApiLogManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-logs']);
$log = $manipulator->create($account, Request::create('/databoxes/list/', 'POST'), new Response());
$countBefore = count(self::$DI['app']['repo.api-logs']->findAll());
$manipulator->delete($log);
$this->assertGreaterThan(count(self::$DI['app']['repo.api-logs']->findAll()), $countBefore);
}
public function testUpdate()
{
$manipulator = new ApiAccountManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-accounts']);
$account = $manipulator->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$manipulator = new ApiLogManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-logs']);
$log = $manipulator->create($account, Request::create('/databoxes/list/', 'POST'), new Response());
$log->setAspect('a-new-aspect');
$manipulator->update($log);
$log = self::$DI['app']['repo.api-logs']->find($log->getId());
$this->assertEquals('a-new-aspect', $log->getAspect());
}
public function apiRouteProvider()
{
return [
['/databoxes/list/', ['resource' => 'databoxes', 'aspect' => null, 'action' => 'list', 'general' => 'databoxes']],
['/databoxes/1/collections/', ['resource' => 'databoxes', 'aspect' => 'collections', 'action' => null, 'general' => 'databoxes']],
['/databoxes/1/status/', ['resource' => 'databoxes', 'aspect' => 'status', 'action' => null, 'general' => 'databoxes']],
['/databoxes/1/metadatas/', ['resource' => 'databoxes', 'aspect' => 'metadatas', 'action' => null, 'general' => 'databoxes']],
['/databoxes/1/termsOfUse/', ['resource' => 'databoxes', 'aspect' => 'termsOfUse', 'action' => null, 'general' => 'databoxes']],
['/quarantine/list/', ['resource' => 'quarantine', 'aspect' => null, 'action' => 'list', 'general' => 'quarantine']],
['/records/add/', ['resource' => 'records', 'aspect' => null, 'action' => 'add', 'general' => 'records']],
['/records/search/', ['resource' => 'records', 'aspect' => null, 'action' => 'search', 'general' => 'records']],
['/records/1/1/caption/', ['resource' => 'records', 'aspect' => 'caption', 'action' => null, 'general' => 'records']],
['/records/1/1/metadatas/', ['resource' => 'records', 'aspect' => 'metadatas', 'action' => null, 'general' => 'records']],
['/records/1/1/status/', ['resource' => 'records', 'aspect' => 'status', 'action' => null, 'general' => 'records']],
['/records/1/1/embed/', ['resource' => 'records', 'aspect' => 'embed', 'action' => null, 'general' => 'records']],
['/records/1/1/related/', ['resource' => 'records', 'aspect' => 'related', 'action' => null, 'general' => 'records']],
['/records/1/1/', ['resource' => 'records', 'aspect' => null, 'action' => 'get', 'general' => 'records']],
['/records/1/1/setstatus/', ['resource' => 'records', 'aspect' => null, 'action' => 'setstatus', 'general' => 'records']],
['/records/1/1/setmetadatas/', ['resource' => 'records', 'aspect' => null, 'action' => 'setmetadatas', 'general' => 'records']],
['/records/1/1/setcollection/', ['resource' => 'records', 'aspect' => null, 'action' => 'setcollection', 'general' => 'records']],
['/stories/1/1/embed/', ['resource' => 'stories', 'aspect' => 'embed', 'action' => null, 'general' => 'stories']],
['/stories/1/1/', ['resource' => 'stories', 'aspect' => null, 'action' => 'get', 'general' => 'stories']],
['/baskets/add/', ['resource' => 'baskets', 'aspect' => null, 'action' => 'add', 'general' => 'baskets']],
['/baskets/1/content/', ['resource' => 'baskets', 'aspect' => 'content', 'action' => '', 'general' => 'baskets']],
['/baskets/1/delete/', ['resource' => 'baskets', 'aspect' => null, 'action' => 'delete', 'general' => 'baskets']],
['/baskets/1/setdescription/', ['resource' => 'baskets', 'aspect' => null, 'action' => 'setdescription', 'general' => 'baskets']],
['/baskets/1/setname/', ['resource' => 'baskets', 'aspect' => null, 'action' => 'setname', 'general' => 'baskets']],
['/feeds/list/', ['resource' => 'feeds', 'aspect' => null, 'action' => 'list', 'general' => 'feeds']],
['/feeds/1/content/', ['resource' => 'feeds', 'aspect' => 'content', 'action' => null, 'general' => 'feeds']],
['/feeds/content/', ['resource' => 'feeds', 'aspect' => 'content', 'action' => null, 'general' => 'feeds']],
['/feeds/entry/1/', ['resource' => 'feeds', 'aspect' => 'entry', 'action' => null, 'general' => 'feeds']],
['/monitor/phraseanet/', ['resource' => null, 'aspect' => 'phraseanet', 'action' => null, 'general' => 'monitor']],
['/monitor/scheduler/', ['resource' => null, 'aspect' => 'scheduler', 'action' => null, 'general' => 'monitor']],
['/monitor/task/1/', ['resource' => null, 'aspect' => 'task', 'action' => 'get', 'general' => 'monitor']],
['/monitor/task/1/stop/', ['resource' => null, 'aspect' => 'task', 'action' => 'stop', 'general' => 'monitor']],
['/monitor/task/1/start/', ['resource' => null, 'aspect' => 'task', 'action' => 'start', 'general' => 'monitor']],
['/monitor/tasks/', ['resource' => null, 'aspect' => 'tasks', 'action' => null, 'general' => 'monitor']],
];
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Alchemy\Tests\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Manipulator\ApiOauthCodeManipulator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ApiOauthCodeManipulatorTest extends \PhraseanetTestCase
{
public function testCreate()
{
$manipulator = new ApiOauthCodeManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-oauth-codes'], self::$DI['app']['random.medium']);
$nbCodes = count(self::$DI['app']['repo.api-oauth-codes']->findAll());
$account = self::$DI['app']['manipulator.api-account']->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$manipulator->create($account, 'http://www.redirect.url', time() + 30);
$this->assertGreaterThan($nbCodes, count(self::$DI['app']['repo.api-oauth-codes']->findAll()));
}
public function testDelete()
{
$manipulator = new ApiOauthCodeManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-oauth-codes'], self::$DI['app']['random.medium']);
$account = self::$DI['app']['manipulator.api-account']->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$code = $manipulator->create($account, 'http://www.redirect.url', time() + 30);
$countBefore = count(self::$DI['app']['repo.api-oauth-codes']->findAll());
$manipulator->delete($code);
$this->assertGreaterThan(count(self::$DI['app']['repo.api-oauth-codes']->findAll()), $countBefore);
}
public function testUpdate()
{
$manipulator = new ApiOauthCodeManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-oauth-codes'], self::$DI['app']['random.medium']);
$account = self::$DI['app']['manipulator.api-account']->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$code = $manipulator->create($account, 'http://www.redirect.url', $t = time() + 30);
$code->setExpires(time() + 40);
$manipulator->update($code);
$code = self::$DI['app']['repo.api-oauth-codes']->find($code->getCode());
$this->assertGreaterThan($t, $code->getExpires());
}
/**
* @setExpectedException Alchemy\Phrasea\Exception\InvalidArgumentException
*/
public function testSetRedirectUriBadArgumentException()
{
$manipulator = new ApiOauthCodeManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-oauth-codes'], self::$DI['app']['random.medium']);
$account = self::$DI['app']['manipulator.api-account']->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$code = $manipulator->create($account, 'http://www.redirect.url', time() + 30);
try {
$manipulator->setRedirectUri($code, 'bad-url');
$this->fail('Invalid argument exception should be raised');
} catch (InvalidArgumentException $e) {
}
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Alchemy\Tests\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Model\Manipulator\ApiLogManipulator;
use Alchemy\Phrasea\Model\Manipulator\ApiOauthTokenManipulator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ApiOauthTokenManipulatorTest extends \PhraseanetTestCase
{
public function testCreate()
{
$manipulator = new ApiOauthTokenManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-oauth-tokens'], self::$DI['app']['random.medium']);
$nbTokens = count(self::$DI['app']['repo.api-oauth-tokens']->findAll());
$account = self::$DI['app']['manipulator.api-account']->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$manipulator->create($account);
$this->assertGreaterThan($nbTokens, count(self::$DI['app']['repo.api-oauth-tokens']->findAll()));
}
public function testDelete()
{
$manipulator = new ApiOauthTokenManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-oauth-tokens'], self::$DI['app']['random.medium']);
$account = self::$DI['app']['manipulator.api-account']->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$token = $manipulator->create($account);
$countBefore = count(self::$DI['app']['repo.api-oauth-tokens']->findAll());
$manipulator->delete($token);
$this->assertGreaterThan(count(self::$DI['app']['repo.api-oauth-tokens']->findAll()), $countBefore);
}
public function testUpdate()
{
$manipulator = new ApiOauthTokenManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-oauth-tokens'], self::$DI['app']['random.medium']);
$account = self::$DI['app']['manipulator.api-account']->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$token = $manipulator->create($account);
$token->setSessionId(123456);
$manipulator->update($token);
$token = self::$DI['app']['repo.api-oauth-tokens']->find($token->getOauthToken());
$this->assertEquals(123456, $token->getSessionId());
}
public function testRenew()
{
$manipulator = new ApiOauthTokenManipulator(self::$DI['app']['EM'], self::$DI['app']['repo.api-oauth-tokens'], self::$DI['app']['random.medium']);
$account = self::$DI['app']['manipulator.api-account']->create(self::$DI['oauth2-app-user'], self::$DI['user']);
$token = $manipulator->create($account);
$oauthTokenBefore = $token->getOauthToken();
$manipulator->renew($token);
$this->assertNotEquals($oauthTokenBefore, $token->getOauthToken());
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Alchemy\Tests\Phrasea\Model\Repositories;
class ApiAccountRepositoryTest extends \PhraseanetTestCase
{
public function testFindByUserAndApplication()
{
$acc = self::$DI['app']['EM']->getRepository('Phraseanet:ApiAccount')->findByUserAndApplication(self::$DI['user_notAdmin'], self::$DI['oauth2-app-user-not-admin']);
$this->assertEquals(1, count($acc));
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Alchemy\Tests\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
class ApiApplicationRepositoryTest extends \PhraseanetTestCase
{
public function testFindByCreator()
{
$app = self::$DI['app']['EM']->getRepository('Phraseanet:ApiApplication')->findByCreator(self::$DI['user']);
$this->assertCount(1, $app);
}
public function testFindByUser()
{
$app = self::$DI['app']['EM']->getRepository('Phraseanet:ApiApplication')->findByUser(self::$DI['user']);
$this->assertCount(1, $app);
}
public function testFindAuthorizedAppsByUser()
{
$app = self::$DI['app']['EM']->getRepository('Phraseanet:ApiApplication')->findAuthorizedAppsByUser(self::$DI['user']);
$this->assertCount(1, $app);
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Alchemy\Tests\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
class ApiOauthCodeRepositoryTest extends \PhraseanetTestCase
{
public function testFindByAccount()
{
self::$DI['app']['manipulator.api-oauth-code']->create(self::$DI['oauth2-app-acc-user'], 'http://www.callback.fr', time() + 40);
$codes = self::$DI['app']['EM']->getRepository('Phraseanet:ApiOauthCode')->findByAccount(self::$DI['oauth2-app-acc-user']);
$this->assertGreaterThan(0, count($codes));
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Alchemy\Tests\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
class ApiOauthTokenRepositoryTest extends \PhraseanetTestCase
{
public function testFindDeveloperToken()
{
$tok = self::$DI['app']['EM']->getRepository('Phraseanet:ApiOauthToken')->findByAccount(self::$DI['oauth2-app-acc-user']);
$this->assertNotNull($tok);
}
public function testFindOauthTokens()
{
$tokens = self::$DI['app']['EM']->getRepository('Phraseanet:ApiOauthToken')->findOauthTokens(self::$DI['oauth2-app-acc-user']);
$this->assertGreaterThan(0, count($tokens));
}
}

View File

@@ -199,11 +199,19 @@ abstract class PhraseanetTestCase extends WebTestCase
}); });
self::$DI['oauth2-app-user'] = self::$DI->share(function ($DI) { self::$DI['oauth2-app-user'] = self::$DI->share(function ($DI) {
return new \API_OAuth2_Application($DI['app'], self::$fixtureIds['oauth']['user']); return $DI['app']['repo.api-applications']->find(self::$fixtureIds['oauth']['user']);
}); });
self::$DI['oauth2-app-user_notAdmin'] = self::$DI->share(function ($DI) { self::$DI['oauth2-app-user-not-admin'] = self::$DI->share(function ($DI) {
return new \API_OAuth2_Application($DI['app'], self::$fixtureIds['oauth']['user_notAdmin']); return $DI['app']['repo.api-applications']->find(self::$fixtureIds['oauth']['user-not-admin']);
});
self::$DI['oauth2-app-acc-user'] = self::$DI->share(function ($DI) {
return $DI['app']['repo.api-accounts']->find(self::$fixtureIds['oauth']['acc-user']);
});
self::$DI['oauth2-app-acc-user-not-admin'] = self::$DI->share(function ($DI) {
return $DI['app']['repo.api-accounts']->find(self::$fixtureIds['oauth']['acc-user-not-admin']);
}); });
self::$DI['logger'] = self::$DI->share(function () { self::$DI['logger'] = self::$DI->share(function () {

View File

@@ -1,45 +0,0 @@
<?php
class api_oauthv2_AccountTest extends \PhraseanetTestCase
{
/**
* @var API_OAuth2_Account
*/
protected $object;
public function setUp()
{
parent::setUp();
$this->object = API_OAuth2_Account::load_with_user(self::$DI['app'], self::$DI['oauth2-app-user'], self::$DI['user']);
}
public function testGettersAndSetters()
{
$this->assertTrue(is_int($this->object->get_id()));
$this->assertInstanceOf('Alchemy\Phrasea\Model\Entities\User', $this->object->get_user());
$this->assertEquals(self::$DI['user']->getId(), $this->object->get_user()->getId());
$this->assertEquals('1.0', $this->object->get_api_version());
$this->assertTrue(is_bool($this->object->is_revoked()));
$this->object->set_revoked(true);
$this->assertTrue($this->object->is_revoked());
$this->object->set_revoked(false);
$this->assertFalse($this->object->is_revoked());
$this->assertInstanceOf('DateTime', $this->object->get_created_on());
$this->assertInstanceOf('API_OAuth2_Token', $this->object->get_token());
$this->assertInstanceOf('API_OAuth2_Application', $this->object->get_application());
$this->assertEquals(self::$DI['oauth2-app-user'], $this->object->get_application());
}
public function testLoad_with_user()
{
$loaded = API_OAuth2_Account::load_with_user(self::$DI['app'], self::$DI['oauth2-app-user'], self::$DI['user']);
$this->assertInstanceOf('API_OAuth2_Account', $loaded);
$this->assertEquals($this->object, $loaded);
}
}

View File

@@ -1,117 +0,0 @@
<?php
class api_oauthv2_ApplicationTest extends \PhraseanetTestCase
{
public function testLoad_from_client_id()
{
$client_id = self::$DI['oauth2-app-user']->get_client_id();
$loaded = API_OAuth2_Application::load_from_client_id(self::$DI['app'], $client_id);
$this->assertInstanceOf('API_OAuth2_Application', $loaded);
$this->assertEquals(self::$DI['oauth2-app-user'], $loaded);
}
public function testLoad_dev_app_by_user()
{
$apps = API_OAuth2_Application::load_dev_app_by_user(self::$DI['app'], self::$DI['user']);
$this->assertTrue(is_array($apps));
$this->assertTrue(count($apps) > 0);
$found = false;
foreach ($apps as $app) {
if ($app->get_id() === self::$DI['oauth2-app-user']->get_id())
$found = true;
$this->assertInstanceOf('API_OAuth2_Application', $app);
}
if ( ! $found)
$this->fail();
}
public function testLoad_app_by_user()
{
$apps = API_OAuth2_Application::load_app_by_user(self::$DI['app'], self::$DI['user']);
$this->assertTrue(is_array($apps));
$this->assertTrue(count($apps) > 0);
$found = false;
foreach ($apps as $app) {
if ($app->get_id() === self::$DI['oauth2-app-user']->get_id())
$found = true;
$this->assertInstanceOf('API_OAuth2_Application', $app);
}
if ( ! $found)
$this->fail();
}
public function testGettersAndSetters()
{
$this->assertTrue(is_int(self::$DI['oauth2-app-user']->get_id()));
$this->assertInstanceOf('Alchemy\Phrasea\Model\Entities\User', self::$DI['oauth2-app-user']->get_creator());
$this->assertEquals(self::$DI['user']->getId(), self::$DI['oauth2-app-user']->get_creator()->getId());
$this->assertTrue(in_array(self::$DI['oauth2-app-user']->get_type(), [API_OAuth2_Application::DESKTOP_TYPE, API_OAuth2_Application::WEB_TYPE]));
$this->assertTrue(is_string(self::$DI['oauth2-app-user']->get_nonce()));
$this->assertEquals(64, strlen(self::$DI['oauth2-app-user']->get_nonce()));
try {
self::$DI['oauth2-app-user']->set_type('prout');
$this->fail();
} catch (Exception_InvalidArgument $e) {
}
self::$DI['oauth2-app-user']->set_type(API_OAuth2_Application::WEB_TYPE);
$this->assertEquals(API_OAuth2_Application::WEB_TYPE, self::$DI['oauth2-app-user']->get_type());
self::$DI['oauth2-app-user']->set_type(API_OAuth2_Application::DESKTOP_TYPE);
$this->assertEquals(API_OAuth2_Application::DESKTOP_TYPE, self::$DI['oauth2-app-user']->get_type());
$this->assertEquals(API_OAuth2_Application::NATIVE_APP_REDIRECT_URI, self::$DI['oauth2-app-user']->get_redirect_uri());
self::$DI['oauth2-app-user']->set_type(API_OAuth2_Application::WEB_TYPE);
self::$DI['oauth2-app-user']->set_name('prout');
$this->assertEquals('prout', self::$DI['oauth2-app-user']->get_name());
self::$DI['oauth2-app-user']->set_name('test application for user');
$this->assertEquals('test application for user', self::$DI['oauth2-app-user']->get_name());
$desc = 'prouti prouto prout prout';
self::$DI['oauth2-app-user']->set_description($desc);
$this->assertEquals($desc, self::$DI['oauth2-app-user']->get_description());
self::$DI['oauth2-app-user']->set_description('');
$this->assertEquals('', self::$DI['oauth2-app-user']->get_description());
$site = 'http://www.example.com/';
self::$DI['oauth2-app-user']->set_website($site);
$this->assertEquals($site, self::$DI['oauth2-app-user']->get_website());
self::$DI['oauth2-app-user']->set_website('');
$this->assertEquals('', self::$DI['oauth2-app-user']->get_website());
$this->assertInstanceOf('DateTime', self::$DI['oauth2-app-user']->get_created_on());
$this->assertInstanceOf('DateTime', self::$DI['oauth2-app-user']->get_last_modified());
$this->assertMd5(self::$DI['oauth2-app-user']->get_client_id());
$client_id = md5('prouto');
self::$DI['oauth2-app-user']->set_client_id($client_id);
$this->assertEquals($client_id, self::$DI['oauth2-app-user']->get_client_id());
$this->assertMd5(self::$DI['oauth2-app-user']->get_client_id());
$this->assertMd5(self::$DI['oauth2-app-user']->get_client_secret());
$client_secret = md5('prouto');
self::$DI['oauth2-app-user']->set_client_secret($client_secret);
$this->assertEquals($client_secret, self::$DI['oauth2-app-user']->get_client_secret());
$this->assertMd5(self::$DI['oauth2-app-user']->get_client_secret());
$uri = 'http://www.example.com/callback/';
self::$DI['oauth2-app-user']->set_redirect_uri($uri);
$this->assertEquals($uri, self::$DI['oauth2-app-user']->get_redirect_uri());
$this->assertInstanceOf('API_OAuth2_Account', self::$DI['oauth2-app-user']->get_user_account(self::$DI['user']));
}
private function assertmd5($md5)
{
$this->assertTrue((count(preg_match('/[a-z0-9]{32}/', $md5)) === 1));
}
}

View File

@@ -1,70 +0,0 @@
<?php
class api_oauthv2_AuthCodeTest extends \PhraseanetTestCase
{
/**
* @var API_OAuth2_AuthCode
*/
protected $object;
protected $code;
protected $account;
protected $application;
public function setUp()
{
parent::setUp();
$this->account = API_OAuth2_Account::load_with_user(self::$DI['app'], self::$DI['oauth2-app-user'], self::$DI['user']);
$expires = time() + 100;
$this->code = self::$DI['app']['random.low']->generateString(8);
$this->object = API_OAuth2_AuthCode::create(self::$DI['app'], $this->account, $this->code, $expires);
}
public function testGet_code()
{
$this->assertEquals($this->code, $this->object->get_code());
}
public function testGet_account()
{
$this->assertInstanceOf('API_OAuth2_Account', $this->object->get_account());
}
public function testGet_redirect_uri()
{
$this->assertEquals('', $this->object->get_redirect_uri());
}
public function testSet_redirect_uri()
{
$redirect_uri = 'https://www.google.com';
$this->assertEquals('', $this->object->get_redirect_uri());
$this->object->set_redirect_uri($redirect_uri);
$this->assertEquals($redirect_uri, $this->object->get_redirect_uri());
}
public function testGet_expires()
{
$this->assertInternalType('string', $this->object->get_expires());
}
public function testGet_scope()
{
$this->assertEquals('', $this->object->get_scope());
}
public function testSet_scope()
{
$scope = 'prout';
$this->assertEquals('', $this->object->get_scope());
$this->object->set_scope($scope);
$this->assertEquals($scope, $this->object->get_scope());
}
public function testLoad_codes_by_account()
{
$this->assertTrue(is_array(API_OAuth2_AuthCode::load_codes_by_account(self::$DI['app'], $this->account)));
$this->assertTrue(count(API_OAuth2_AuthCode::load_codes_by_account(self::$DI['app'], $this->account)) > 0);
}
}

View File

@@ -1,96 +0,0 @@
<?php
class api_oauthv2_RefreshTokenTest extends \PhraseanetTestCase
{
/**
* @var API_OAuth2_RefreshToken
*/
protected $object;
protected $token;
protected $scope;
protected $account;
public function setUp()
{
parent::setUp();
$this->account = API_OAuth2_Account::load_with_user(self::$DI['app'], self::$DI['oauth2-app-user'], self::$DI['user']);
$expires = time() + 100;
$this->token = self::$DI['app']['random.low']->generateString(8);
$this->scope = 'scopidou';
$this->object = API_OAuth2_RefreshToken::create(self::$DI['app'], $this->account, $expires, $this->token, $this->scope);
}
public function testGet_value()
{
$this->assertEquals($this->token, $this->object->get_value());
}
/**
* @todo Implement testGet_account().
*/
public function testGet_account()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
/**
* @todo Implement testGet_expires().
*/
public function testGet_expires()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
/**
* @todo Implement testGet_scope().
*/
public function testGet_scope()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
/**
* @todo Implement testDelete().
*/
public function testDelete()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
/**
* @todo Implement testLoad_by_account().
*/
public function testLoad_by_account()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
/**
* @todo Implement testCreate().
*/
public function testCreate()
{
// Remove the following lines when you implement this test.
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
}

View File

@@ -1,88 +0,0 @@
<?php
class api_oauthv2_TokenTest extends \PhraseanetTestCase
{
/**
* @var API_OAuth2_Token
*/
protected $object;
public function setUp()
{
parent::setUp();
$account = API_OAuth2_Account::load_with_user(self::$DI['app'], self::$DI['oauth2-app-user'], self::$DI['user']);
try {
new API_OAuth2_Token(self::$DI['app']['phraseanet.appbox'], $account, self::$DI['app']['random.medium']);
$this->fail();
} catch (Exception $e) {
}
$this->object = API_OAuth2_Token::create(self::$DI['app']['phraseanet.appbox'], $account, self::$DI['app']['random.medium']);
}
public function tearDown()
{
$this->object->delete();
parent::tearDown();
}
private function assertmd5($md5)
{
$this->assertTrue((count(preg_match('/[a-z0-9]{32}/', $md5)) === 1));
}
public function testGettersAndSetters()
{
$this->assertmd5($this->object->get_value());
$value = md5('prout');
$this->object->set_value($value);
$this->assertEquals($value, $this->object->get_value());
$this->object->set_session_id(null);
$this->assertNull($this->object->get_session_id());
$this->object->set_session_id(458);
$this->assertEquals(458, $this->object->get_session_id());
$expire = time() + 3600;
$this->object->set_expires($expire);
$diff = (int) $this->object->get_expires() - time();
$this->assertSame($expire, $this->object->get_expires(), "expiration timestamp is string : " . $this->object->get_expires());
$this->assertTrue($diff > 3500, "expire value $diff should be more than 3500 seconds ");
$this->assertTrue($diff < 3700, "expire value $diff should be less than 3700 seconds ");
$date = time() + 7200;
$this->object->set_expires($date);
$this->assertEquals($date, $this->object->get_expires());
$this->assertNull($this->object->get_scope());
$this->assertNull($this->object->get_scope());
$scope = "prout";
$this->object->set_scope($scope);
$this->assertEquals($scope, $this->object->get_scope());
$this->assertInstanceOf('API_OAuth2_Account', $this->object->get_account());
}
public function testRenew()
{
$first = $this->object->get_value();
$this->assertMd5($first);
$this->object->renew();
$second = $this->object->get_value();
$this->assertMd5($second);
$this->assertNotEquals($second, $first);
}
public function testLoad_by_oauth_token()
{
$token = $this->object->get_value();
$loaded = API_OAuth2_Token::load_by_oauth_token(self::$DI['app'], $token);
$this->assertInstanceOf('API_OAuth2_Token', $loaded);
$this->assertEquals($this->object, $loaded);
}
}

View File

@@ -7,7 +7,7 @@ $(document).ready(function () {
type: "GET", type: "GET",
url: $this.attr("href"), url: $this.attr("href"),
dataType: 'json', dataType: 'json',
data: {revoke: $this.hasClass("authorize") ? 1 : 0}, data: {revoke: $this.hasClass("authorize") ? 0 : 1},
success: function (data) { success: function (data) {
if (data.success) { if (data.success) {
var li = $this.closest('li'); var li = $this.closest('li');