Add Permalink

This commit is contained in:
Benoît Burnichon
2015-05-20 22:02:18 +02:00
parent 2a3e31ffa6
commit fb5ca52306
11 changed files with 384 additions and 71 deletions

View File

@@ -45,7 +45,7 @@
"doctrine/orm": "~2.4.0", "doctrine/orm": "~2.4.0",
"elasticsearch/elasticsearch": "~1.0", "elasticsearch/elasticsearch": "~1.0",
"facebook/php-sdk": "~3.0", "facebook/php-sdk": "~3.0",
"firebase/php-jwt": "~2.0", "firebase/php-jwt": "^2.1.0",
"gedmo/doctrine-extensions": "~2.3.0", "gedmo/doctrine-extensions": "~2.3.0",
"goodby/csv": "dev-master", "goodby/csv": "dev-master",
"guzzle/guzzle": "~3.0", "guzzle/guzzle": "~3.0",

53
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "4f67fa38f0a20c5653ce1b1aebec193c", "hash": "0cc03f42a7df15fa96138931fa1a454d",
"packages": [ "packages": [
{ {
"name": "alchemy-fr/tcpdf-clone", "name": "alchemy-fr/tcpdf-clone",
@@ -14,6 +14,12 @@
"url": "https://github.com/alchemy-fr/tcpdf-clone.git", "url": "https://github.com/alchemy-fr/tcpdf-clone.git",
"reference": "2ba0248a7187f1626df6c128750650416267f0e7" "reference": "2ba0248a7187f1626df6c128750650416267f0e7"
}, },
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/alchemy-fr/tcpdf-clone/zipball/2ba0248a7187f1626df6c128750650416267f0e7",
"reference": "2ba0248a7187f1626df6c128750650416267f0e7",
"shasum": ""
},
"require": { "require": {
"php": ">=5.3.0" "php": ">=5.3.0"
}, },
@@ -60,6 +66,10 @@
"qrcode", "qrcode",
"tcpdf" "tcpdf"
], ],
"support": {
"source": "https://github.com/alchemy-fr/tcpdf-clone/tree/6.0.039",
"issues": "https://github.com/alchemy-fr/tcpdf-clone/issues"
},
"time": "2013-10-13 16:11:17" "time": "2013-10-13 16:11:17"
}, },
{ {
@@ -103,7 +113,7 @@
"homepage": "http://www.lickmychip.com/" "homepage": "http://www.lickmychip.com/"
}, },
{ {
"name": "Nicolas Le Goff", "name": "nlegoff",
"email": "legoff.n@gmail.com" "email": "legoff.n@gmail.com"
}, },
{ {
@@ -1260,12 +1270,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/igorw/evenement.git", "url": "https://github.com/igorw/evenement.git",
"reference": "v1.0.0" "reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/igorw/evenement/zipball/fa966683e7df3e5dd5929d984a44abfbd6bafe8d", "url": "https://api.github.com/repos/igorw/evenement/zipball/fa966683e7df3e5dd5929d984a44abfbd6bafe8d",
"reference": "v1.0.0", "reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1292,7 +1302,7 @@
"keywords": [ "keywords": [
"event-dispatcher" "event-dispatcher"
], ],
"time": "2012-05-30 08:01:08" "time": "2012-05-30 15:01:08"
}, },
{ {
"name": "facebook/php-sdk", "name": "facebook/php-sdk",
@@ -1342,17 +1352,16 @@
}, },
{ {
"name": "firebase/php-jwt", "name": "firebase/php-jwt",
"version": "2.0.0", "version": "v2.1.0",
"target-dir": "Firebase/PHP-JWT",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/firebase/php-jwt.git", "url": "https://github.com/firebase/php-jwt.git",
"reference": "ffcfd888ce1e4f2d70cac2dc9b7301038332fe57" "reference": "fb219727e199dd80a72d5274ebb5c8b24d58dd9b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/ffcfd888ce1e4f2d70cac2dc9b7301038332fe57", "url": "https://api.github.com/repos/firebase/php-jwt/zipball/fb219727e199dd80a72d5274ebb5c8b24d58dd9b",
"reference": "ffcfd888ce1e4f2d70cac2dc9b7301038332fe57", "reference": "fb219727e199dd80a72d5274ebb5c8b24d58dd9b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1383,7 +1392,7 @@
], ],
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
"homepage": "https://github.com/firebase/php-jwt", "homepage": "https://github.com/firebase/php-jwt",
"time": "2015-04-01 18:46:38" "time": "2015-05-20 19:16:04"
}, },
{ {
"name": "gedmo/doctrine-extensions", "name": "gedmo/doctrine-extensions",
@@ -2425,7 +2434,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-develop": "0.7-dev" "dev-develop": "0.6-dev"
} }
}, },
"autoload": { "autoload": {
@@ -3080,7 +3089,7 @@
], ],
"authors": [ "authors": [
{ {
"name": "Stephen Clay", "name": "Steve Clay",
"email": "steve@mrclay.org", "email": "steve@mrclay.org",
"homepage": "http://www.mrclay.org/", "homepage": "http://www.mrclay.org/",
"role": "Developer" "role": "Developer"
@@ -3266,21 +3275,21 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/romainneutron/Imagine-Silex-Service-Provider.git", "url": "https://github.com/romainneutron/Imagine-Silex-Service-Provider.git",
"reference": "0.1.2" "reference": "a8a7862ae90419f2b23746cd8436c2310e4eb084"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/romainneutron/Imagine-Silex-Service-Provider/zipball/a8a7862ae90419f2b23746cd8436c2310e4eb084", "url": "https://api.github.com/repos/romainneutron/Imagine-Silex-Service-Provider/zipball/a8a7862ae90419f2b23746cd8436c2310e4eb084",
"reference": "0.1.2", "reference": "a8a7862ae90419f2b23746cd8436c2310e4eb084",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"imagine/imagine": "*", "imagine/imagine": "*",
"php": ">=5.3.3", "php": ">=5.3.3",
"silex/silex": ">=1.0,<2.0" "silex/silex": "~1.0"
}, },
"require-dev": { "require-dev": {
"symfony/browser-kit": ">=2.0,<3.0" "symfony/browser-kit": "~2.0"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@@ -3752,7 +3761,7 @@
"metadata" "metadata"
], ],
"support": { "support": {
"source": "https://github.com/alchemy-fr/PHPExiftool/tree/0.4.1-mwg-metadata-copy" "source": "https://github.com/alchemy-fr/PHPExiftool/tree/dev"
}, },
"time": "2014-10-08 16:09:02" "time": "2014-10-08 16:09:02"
}, },
@@ -3840,7 +3849,9 @@
"authors": [ "authors": [
{ {
"name": "Fabien Potencier", "name": "Fabien Potencier",
"email": "fabien@symfony.com" "email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
} }
], ],
"description": "Pimple is a simple Dependency Injection Container for PHP 5.3", "description": "Pimple is a simple Dependency Injection Container for PHP 5.3",
@@ -3965,7 +3976,7 @@
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/4f444683ebd56702c3138dc1b5dfd07b6e04a167", "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/fef0e8976bb32ee354c92fc75559581db559148d",
"reference": "acc05c93841b23f1d30637093d7f26b9910db10c", "reference": "acc05c93841b23f1d30637093d7f26b9910db10c",
"shasum": "" "shasum": ""
}, },
@@ -4246,7 +4257,7 @@
}, },
{ {
"name": "Phraseanet Team", "name": "Phraseanet Team",
"email": "support@alchemy.fr", "email": "info@alchemy.fr",
"homepage": "http://www.phraseanet.com/" "homepage": "http://www.phraseanet.com/"
} }
], ],

View File

@@ -317,6 +317,7 @@ class Application extends SilexApplication
'Alchemy\Phrasea\ControllerProvider\Prod\MoveCollection' => [], 'Alchemy\Phrasea\ControllerProvider\Prod\MoveCollection' => [],
'Alchemy\Phrasea\ControllerProvider\Datafiles' => [], 'Alchemy\Phrasea\ControllerProvider\Datafiles' => [],
'Alchemy\Phrasea\ControllerProvider\Lightbox' => [], 'Alchemy\Phrasea\ControllerProvider\Lightbox' => [],
'Alchemy\Phrasea\ControllerProvider\MediaAccessor' => [],
'Alchemy\Phrasea\ControllerProvider\Minifier' => [], 'Alchemy\Phrasea\ControllerProvider\Minifier' => [],
'Alchemy\Phrasea\ControllerProvider\Permalink' => [], 'Alchemy\Phrasea\ControllerProvider\Permalink' => [],
'Alchemy\Phrasea\ControllerProvider\Setup' => [], 'Alchemy\Phrasea\ControllerProvider\Setup' => [],
@@ -683,6 +684,9 @@ class Application extends SilexApplication
'/prod/records/movecollection' => 'Alchemy\Phrasea\ControllerProvider\Prod\MoveCollection', '/prod/records/movecollection' => 'Alchemy\Phrasea\ControllerProvider\Prod\MoveCollection',
'/setup' => 'Alchemy\Phrasea\ControllerProvider\Setup', '/setup' => 'Alchemy\Phrasea\ControllerProvider\Setup',
]; ];
// controllers with routes referenced by api
$providers[$this['controller.media_accessor.route_prefix']] = 'Alchemy\Phrasea\ControllerProvider\MediaAccessor';
foreach ($providers as $prefix => $class) { foreach ($providers as $prefix => $class) {
$this->mount($prefix, new $class); $this->mount($prefix, new $class);
} }

View File

@@ -16,6 +16,7 @@ use Alchemy\Phrasea\Controller\Api\Result;
use Alchemy\Phrasea\ControllerProvider\Api\OAuth2; use Alchemy\Phrasea\ControllerProvider\Api\OAuth2;
use Alchemy\Phrasea\ControllerProvider\Api\V1; use Alchemy\Phrasea\ControllerProvider\Api\V1;
use Alchemy\Phrasea\ControllerProvider\Datafiles; use Alchemy\Phrasea\ControllerProvider\Datafiles;
use Alchemy\Phrasea\ControllerProvider\MediaAccessor;
use Alchemy\Phrasea\ControllerProvider\Minifier; use Alchemy\Phrasea\ControllerProvider\Minifier;
use Alchemy\Phrasea\ControllerProvider\Permalink; use Alchemy\Phrasea\ControllerProvider\Permalink;
use Alchemy\Phrasea\Core\Event\ApiResultEvent; use Alchemy\Phrasea\Core\Event\ApiResultEvent;
@@ -121,6 +122,7 @@ return call_user_func(function ($environment = PhraseaApplication::ENV_PROD) {
$app->mount('/datafiles/', new Datafiles()); $app->mount('/datafiles/', new Datafiles());
$app->mount('/api/v1', new V1()); $app->mount('/api/v1', new V1());
$app->mount('/permalink/', new Permalink()); $app->mount('/permalink/', new Permalink());
$app->mount($app['controller.media_accessor.route_prefix'], new MediaAccessor());
$app->mount('/include/minify/', new Minifier()); $app->mount('/include/minify/', new Minifier());
$app['dispatcher'] = $app->share($app->extend('dispatcher', function ($dispatcher, PhraseaApplication $app) { $app['dispatcher'] = $app->share($app->extend('dispatcher', function ($dispatcher, PhraseaApplication $app) {

View File

@@ -37,12 +37,14 @@ use Alchemy\Phrasea\Model\Entities\FeedItem;
use Alchemy\Phrasea\Model\Entities\LazaretCheck; use Alchemy\Phrasea\Model\Entities\LazaretCheck;
use Alchemy\Phrasea\Model\Entities\LazaretFile; use Alchemy\Phrasea\Model\Entities\LazaretFile;
use Alchemy\Phrasea\Model\Entities\LazaretSession; use Alchemy\Phrasea\Model\Entities\LazaretSession;
use Alchemy\Phrasea\Model\Entities\Secret;
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 Alchemy\Phrasea\Model\Entities\ValidationData; use Alchemy\Phrasea\Model\Entities\ValidationData;
use Alchemy\Phrasea\Model\Entities\ValidationParticipant; use Alchemy\Phrasea\Model\Entities\ValidationParticipant;
use Alchemy\Phrasea\Model\Manipulator\TaskManipulator; use Alchemy\Phrasea\Model\Manipulator\TaskManipulator;
use Alchemy\Phrasea\Model\Manipulator\UserManipulator; use Alchemy\Phrasea\Model\Manipulator\UserManipulator;
use Alchemy\Phrasea\Model\Provider\SecretProvider;
use Alchemy\Phrasea\Model\Repositories\ApiOauthTokenRepository; use Alchemy\Phrasea\Model\Repositories\ApiOauthTokenRepository;
use Alchemy\Phrasea\Model\Repositories\BasketRepository; use Alchemy\Phrasea\Model\Repositories\BasketRepository;
use Alchemy\Phrasea\Model\Repositories\FeedEntryRepository; use Alchemy\Phrasea\Model\Repositories\FeedEntryRepository;
@@ -56,6 +58,7 @@ use Alchemy\Phrasea\SearchEngine\SearchEngineSuggestion;
use Alchemy\Phrasea\Status\StatusStructure; use Alchemy\Phrasea\Status\StatusStructure;
use Alchemy\Phrasea\TaskManager\LiveInformation; use Alchemy\Phrasea\TaskManager\LiveInformation;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use JsonSchema\Uri\UriRetriever; use JsonSchema\Uri\UriRetriever;
use JsonSchema\Validator; use JsonSchema\Validator;
use MediaVorus\MediaVorus; use MediaVorus\MediaVorus;
@@ -874,7 +877,7 @@ class V1Controller extends Controller
$record->substitute_subdef($request->get('name'), $media, $this->app, $adapt); $record->substitute_subdef($request->get('name'), $media, $this->app, $adapt);
foreach ($record->get_embedable_medias() as $name => $media) { foreach ($record->get_embedable_medias() as $name => $media) {
if ($name == $request->get('name') && if ($name == $request->get('name') &&
null !== ($subdef = $this->listEmbeddableMedia($record, $media))) { null !== ($subdef = $this->listEmbeddableMedia($request, $record, $media))) {
$ret[] = $subdef; $ret[] = $subdef;
} }
} }
@@ -882,7 +885,7 @@ class V1Controller extends Controller
return Result::create($request, $ret)->createResponse(); return Result::create($request, $ret)->createResponse();
} }
private function listEmbeddableMedia(\record_adapter $record, \media_subdef $media) private function listEmbeddableMedia(Request $request, \record_adapter $record, \media_subdef $media)
{ {
if (!$media->is_physically_present()) { if (!$media->is_physically_present()) {
return null; return null;
@@ -909,7 +912,14 @@ class V1Controller extends Controller
$permalink = null; $permalink = null;
} }
$key = $this->getConf()->get(['main', 'key']); $urlTTL = (int) $request->get(
'subdef_url_ttl',
$this->getConf()->get(['registry', 'general', 'default-subdef-url-ttl'])
);
if ($urlTTL < 0) {
$urlTTL = -1;
}
$issuer = $this->getAuthenticatedUser();
return [ return [
'name' => $media->get_name(), 'name' => $media->get_name(),
@@ -923,9 +933,37 @@ class V1Controller extends Controller
'substituted' => $media->is_substituted(), 'substituted' => $media->is_substituted(),
'created_on' => $media->get_creation_date()->format(DATE_ATOM), 'created_on' => $media->get_creation_date()->format(DATE_ATOM),
'updated_on' => $media->get_modification_date()->format(DATE_ATOM), 'updated_on' => $media->get_modification_date()->format(DATE_ATOM),
'url' => $this->generateSubDefinitionUrl($issuer, $media, $urlTTL),
'url_ttl' => $urlTTL,
]; ];
} }
/**
* @param User $issuer
* @param \media_subdef $subdef
* @param int $url_ttl
* @return string
*/
private function generateSubDefinitionUrl(User $issuer, \media_subdef $subdef, $url_ttl)
{
$payload = [
'iat' => time(),
'iss' => $issuer->getId(),
'sdef' => [$subdef->get_sbas_id(), $subdef->get_record_id(), $subdef->get_name()],
];
if ($url_ttl >= 0) {
$payload['exp'] = $payload['iat'] + $url_ttl;
}
/** @var SecretProvider $provider */
$provider = $this->app['provider.secrets'];
$secret = $provider->getSecretForUser($issuer);
return sprintf('media_accessor', [
'token' => \JWT::encode($payload, $secret->getToken(), 'HS256', $secret->getId()),
]);
}
private function listPermalink(\media_Permalink_Adapter $permalink) private function listPermalink(\media_Permalink_Adapter $permalink)
{ {
$downloadUrl = $permalink->get_url(); $downloadUrl = $permalink->get_url();
@@ -969,7 +1007,7 @@ class V1Controller extends Controller
if ($record->is_grouping()) { if ($record->is_grouping()) {
$ret['results']['stories'][] = $this->listStory($request, $record); $ret['results']['stories'][] = $this->listStory($request, $record);
} else { } else {
$ret['results']['records'][] = $this->listRecord($record); $ret['results']['records'][] = $this->listRecord($request, $record);
} }
} }
@@ -997,7 +1035,7 @@ class V1Controller extends Controller
continue; continue;
} }
$ret['results'][] = $this->listRecord($record); $ret['results'][] = $this->listRecord($request, $record);
} }
return Result::create($request, $ret)->createResponse(); return Result::create($request, $ret)->createResponse();
@@ -1053,11 +1091,11 @@ class V1Controller extends Controller
/** /**
* Retrieve detailed information about one record * Retrieve detailed information about one record
* *
* @param \record_adapter $record * @param Request $request
* * @param \record_adapter $record
* @return array * @return array
*/ */
public function listRecord(\record_adapter $record) public function listRecord(Request $request, \record_adapter $record)
{ {
$technicalInformation = []; $technicalInformation = [];
foreach ($record->get_technical_infos() as $name => $value) { foreach ($record->get_technical_infos() as $name => $value) {
@@ -1074,7 +1112,7 @@ class V1Controller extends Controller
'created_on' => $record->get_creation_date()->format(DATE_ATOM), 'created_on' => $record->get_creation_date()->format(DATE_ATOM),
'collection_id' => \phrasea::collFromBas($this->app, $record->get_base_id()), 'collection_id' => \phrasea::collFromBas($this->app, $record->get_base_id()),
'sha256' => $record->get_sha256(), 'sha256' => $record->get_sha256(),
'thumbnail' => $this->listEmbeddableMedia($record, $record->get_thumbnail()), 'thumbnail' => $this->listEmbeddableMedia($request, $record, $record->get_thumbnail()),
'technical_informations' => $technicalInformation, 'technical_informations' => $technicalInformation,
'phrasea_type' => $record->get_type(), 'phrasea_type' => $record->get_type(),
'uuid' => $record->get_uuid(), 'uuid' => $record->get_uuid(),
@@ -1095,8 +1133,8 @@ class V1Controller extends Controller
return Result::createError($request, 404, 'Story not found')->createResponse(); return Result::createError($request, 404, 'Story not found')->createResponse();
} }
$records = array_map(function (\record_adapter $record) { $records = array_map(function (\record_adapter $record) use ($request) {
return $this->listRecord($record); return $this->listRecord($request, $record);
}, array_values($story->get_children()->get_elements())); }, array_values($story->get_children()->get_elements()));
$caption = $story->get_caption(); $caption = $story->get_caption();
@@ -1119,7 +1157,7 @@ class V1Controller extends Controller
'updated_on' => $story->get_modification_date()->format(DATE_ATOM), 'updated_on' => $story->get_modification_date()->format(DATE_ATOM),
'created_on' => $story->get_creation_date()->format(DATE_ATOM), 'created_on' => $story->get_creation_date()->format(DATE_ATOM),
'collection_id' => \phrasea::collFromBas($this->app, $story->get_base_id()), 'collection_id' => \phrasea::collFromBas($this->app, $story->get_base_id()),
'thumbnail' => $this->listEmbeddableMedia($story, $story->get_thumbnail()), 'thumbnail' => $this->listEmbeddableMedia($request, $story, $story->get_thumbnail()),
'uuid' => $story->get_uuid(), 'uuid' => $story->get_uuid(),
'metadatas' => [ 'metadatas' => [
'@entity@' => self::OBJECT_TYPE_STORY_METADATA_BAG, '@entity@' => self::OBJECT_TYPE_STORY_METADATA_BAG,
@@ -1364,8 +1402,8 @@ class V1Controller extends Controller
$devices = $request->get('devices', []); $devices = $request->get('devices', []);
$mimes = $request->get('mimes', []); $mimes = $request->get('mimes', []);
$ret = array_filter(array_map(function ($media) use ($record) { $ret = array_filter(array_map(function ($media) use ($request, $record) {
return $this->listEmbeddableMedia($record, $media); return $this->listEmbeddableMedia($request, $record, $media);
}, $record->get_embedable_medias($devices, $mimes))); }, $record->get_embedable_medias($devices, $mimes)));
return Result::create($request, ["embed" => $ret])->createResponse(); return Result::create($request, ["embed" => $ret])->createResponse();
@@ -1450,7 +1488,7 @@ class V1Controller extends Controller
try { try {
$record = $this->findDataboxById($databox_id)->get_record($record_id); $record = $this->findDataboxById($databox_id)->get_record($record_id);
return Result::create($request, ['record' => $this->listRecord($record)])->createResponse(); return Result::create($request, ['record' => $this->listRecord($request, $record)])->createResponse();
} catch (NotFoundHttpException $e) { } catch (NotFoundHttpException $e) {
return Result::createError($request, 404, $this->app->trans('Record Not Found'))->createResponse(); return Result::createError($request, 404, $this->app->trans('Record Not Found'))->createResponse();
} catch (\Exception $e) { } catch (\Exception $e) {
@@ -1476,7 +1514,7 @@ class V1Controller extends Controller
$collection = \collection::get_from_base_id($this->app, $request->get('base_id')); $collection = \collection::get_from_base_id($this->app, $request->get('base_id'));
$record->move_to_collection($collection, $this->getApplicationBox()); $record->move_to_collection($collection, $this->getApplicationBox());
return Result::create($request, ["record" => $this->listRecord($record)])->createResponse(); return Result::create($request, ["record" => $this->listRecord($request, $record)])->createResponse();
} catch (\Exception $e) { } catch (\Exception $e) {
return $this->getBadRequestAction($this->app, $request, $e->getMessage()); return $this->getBadRequestAction($this->app, $request, $e->getMessage());
} }
@@ -1548,7 +1586,7 @@ class V1Controller extends Controller
{ {
$ret = [ $ret = [
"basket" => $this->listBasket($basket), "basket" => $this->listBasket($basket),
"basket_elements" => $this->listBasketContent($basket) "basket_elements" => $this->listBasketContent($request, $basket)
]; ];
return Result::create($request, $ret)->createResponse(); return Result::create($request, $ret)->createResponse();
@@ -1565,30 +1603,30 @@ class V1Controller extends Controller
/** /**
* Retrieve elements of one basket * Retrieve elements of one basket
* *
* @param Basket $Basket * @param Request $request
* * @param Basket $basket
* @return array * @return array
*/ */
private function listBasketContent(Basket $Basket) private function listBasketContent(Request $request, Basket $basket)
{ {
return array_map(function (BasketElement $element) { return array_map(function (BasketElement $element) use ($request) {
return $this->listBasketElement($element); return $this->listBasketElement($request, $element);
}, iterator_to_array($Basket->getElements())); }, iterator_to_array($basket->getElements()));
} }
/** /**
* Retrieve detailed information about a basket element * Retrieve detailed information about a basket element
* *
* @param BasketElement $basket_element * @param Request $request
* * @param BasketElement $basket_element
* @return array * @return array
*/ */
private function listBasketElement(BasketElement $basket_element) private function listBasketElement(Request $request, BasketElement $basket_element)
{ {
$ret = [ $ret = [
'basket_element_id' => $basket_element->getId(), 'basket_element_id' => $basket_element->getId(),
'order' => $basket_element->getOrd(), 'order' => $basket_element->getOrd(),
'record' => $this->listRecord($basket_element->getRecord($this->app)), 'record' => $this->listRecord($request, $basket_element->getRecord($this->app)),
'validation_item' => null != $basket_element->getBasket()->getValidation(), 'validation_item' => null != $basket_element->getBasket()->getValidation(),
]; ];
@@ -1750,7 +1788,7 @@ class V1Controller extends Controller
'total_entries' => $feed->getCountTotalEntries(), 'total_entries' => $feed->getCountTotalEntries(),
'offset_start' => $offset_start, 'offset_start' => $offset_start,
'per_page' => $per_page, 'per_page' => $per_page,
'entries' => $this->listPublicationsEntries($feed, $offset_start, $per_page), 'entries' => $this->listPublicationsEntries($request, $feed, $offset_start, $per_page),
]; ];
return Result::create($request, $data)->createResponse(); return Result::create($request, $data)->createResponse();
@@ -1759,30 +1797,30 @@ class V1Controller extends Controller
/** /**
* Retrieve all entries of one feed * Retrieve all entries of one feed
* *
* @param FeedInterface $feed * @param Request $request
* @param int $offset_start * @param FeedInterface $feed
* @param int $how_many * @param int $offset_start
* * @param int $how_many
* @return array * @return array
*/ */
private function listPublicationsEntries(FeedInterface $feed, $offset_start = 0, $how_many = 5) private function listPublicationsEntries(Request $request, FeedInterface $feed, $offset_start = 0, $how_many = 5)
{ {
return array_map(function ($entry) { return array_map(function ($entry) use ($request) {
return $this->listPublicationEntry($entry); return $this->listPublicationEntry($request, $entry);
}, $feed->getEntries($offset_start, $how_many)); }, $feed->getEntries($offset_start, $how_many));
} }
/** /**
* Retrieve detailed information about one feed entry * Retrieve detailed information about one feed entry
* *
* @param FeedEntry $entry * @param Request $request
* * @param FeedEntry $entry
* @return array * @return array
*/ */
private function listPublicationEntry(FeedEntry $entry) private function listPublicationEntry(Request $request, FeedEntry $entry)
{ {
$items = array_map(function ($item) { $items = array_map(function ($item) use ($request) {
return $this->listPublicationEntryItem($item); return $this->listPublicationEntryItem($request, $item);
}, iterator_to_array($entry->getItems())); }, iterator_to_array($entry->getItems()));
return [ return [
@@ -1804,15 +1842,15 @@ class V1Controller extends Controller
/** /**
* Retrieve detailed information about one feed entry item * Retrieve detailed information about one feed entry item
* *
* @param FeedItem $item * @param Request $request
* * @param FeedItem $item
* @return array * @return array
*/ */
private function listPublicationEntryItem(FeedItem $item) private function listPublicationEntryItem(Request $request, FeedItem $item)
{ {
return [ return [
'item_id' => $item->getId(), 'item_id' => $item->getId(),
'record' => $this->listRecord($item->getRecord($this->app)), 'record' => $this->listRecord($request, $item->getRecord($this->app)),
]; ];
} }
@@ -1829,7 +1867,7 @@ class V1Controller extends Controller
return Result::createError($request, 403, 'You have not access to the parent feed')->createResponse(); return Result::createError($request, 403, 'You have not access to the parent feed')->createResponse();
} }
return Result::create($request, ['entry' => $this->listPublicationEntry($entry)])->createResponse(); return Result::create($request, ['entry' => $this->listPublicationEntry($request, $entry)])->createResponse();
} }
/** /**
@@ -1861,7 +1899,7 @@ class V1Controller extends Controller
'feed' => $this->listPublication($feed, $user), 'feed' => $this->listPublication($feed, $user),
'offset_start' => $offset_start, 'offset_start' => $offset_start,
'per_page' => $per_page, 'per_page' => $per_page,
'entries' => $this->listPublicationsEntries($feed, $offset_start, $per_page), 'entries' => $this->listPublicationsEntries($request, $feed, $offset_start, $per_page),
]; ];
return Result::create($request, $data)->createResponse(); return Result::create($request, $data)->createResponse();
@@ -1883,8 +1921,8 @@ class V1Controller extends Controller
$devices = $request->get('devices', []); $devices = $request->get('devices', []);
$mimes = $request->get('mimes', []); $mimes = $request->get('mimes', []);
$ret = array_filter(array_map(function ($media) use ($record) { $ret = array_filter(array_map(function ($media) use ($request, $record) {
return $this->listEmbeddableMedia($record, $media); return $this->listEmbeddableMedia($request, $record, $media);
}, $record->get_embedable_medias($devices, $mimes))); }, $record->get_embedable_medias($devices, $mimes)));
return Result::create($request, ["embed" => $ret])->createResponse(); return Result::create($request, ["embed" => $ret])->createResponse();

View File

@@ -0,0 +1,89 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class MediaAccessorController extends Controller
{
/** @var array|\ArrayAccess */
private $keyStorage = [];
/** @var array */
private $allowedAlgorithms = [];
/**
* @param array|\ArrayAccess $keyStorage
* @return $this
*/
public function setKeyStorage($keyStorage)
{
if (!is_array($keyStorage) && !$keyStorage instanceof \ArrayAccess) {
throw new \InvalidArgumentException(sprintf(
'expects $keyStorage to be an array or an instance of ArrayAccess, got %s',
is_object($keyStorage) ? get_class($keyStorage) : gettype($keyStorage)
));
}
$this->keyStorage = $keyStorage;
return $this;
}
/**
* @param array $allowedAlgorithms
* @return $this
*/
public function setAllowedAlgorithms(array $allowedAlgorithms)
{
$this->allowedAlgorithms = $allowedAlgorithms;
return $this;
}
public function showAction(Request $request, $token)
{
$token = \JWT::decode($token, $this->keyStorage, $this->allowedAlgorithms);
if (! isset($token->sdef) || !is_array($token->sdef) || count($token->sdef) !== 3) {
throw new BadRequestHttpException('sdef should be a sub-definition identifier.');
}
list ($sbas_id, $record_id, $subdef) = $token->sdef;
try {
$databox = $this->findDataboxById($sbas_id);
$record = $databox->get_record($record_id);
$subDefinition = $record->get_subdef($subdef);
$permalink = $subDefinition->get_permalink();
} catch (\Exception $exception) {
throw new NotFoundHttpException('Media was not found', $exception);
}
$subRequest = Request::create(
(string) $permalink->get_url(),
'GET',
[],
$request->cookies->all(),
[],
$request->server->all()
);
if ($request->request->has('download')) {
$subRequest->request->set('download', $request->request-get('download'));
}
$response = $this->app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false);
// Remove Caption link header as it contains permalink token.
$response->headers->remove('link');
return $response;
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\ControllerProvider;
use Alchemy\Phrasea\Controller\MediaAccessorController;
use Alchemy\Phrasea\Model\Entities\Secret;
use Alchemy\Phrasea\Model\Provider\DefaultSecretProvider;
use Doctrine\ORM\EntityManagerInterface;
use Silex\Application;
use Silex\ControllerProviderInterface;
use Silex\ServiceProviderInterface;
class MediaAccessor implements ServiceProviderInterface, ControllerProviderInterface
{
use ControllerProviderTrait;
public function register(Application $app)
{
$app['repo.secrets'] = $app->share(function (Application $app) {
/** @var EntityManagerInterface $manager */
$manager = $app['orm.em'];
return $manager->getRepository(Secret::class);
});
$app['provider.secrets'] = $app->share(function (Application $app) {
return new DefaultSecretProvider($app['repo.secrets'], $app['random.medium']);
});
$app['controller.media_accessor'] = $app->share(function (Application $app) {
return (new MediaAccessorController($app))
->setAllowedAlgorithms(['HS256'])
->setKeyStorage($app['repo.secrets']);
});
$app['controller.media_accessor.route_prefix'] = '/medias';
}
public function boot(Application $app)
{
}
public function connect(Application $app)
{
$controllers = $this->createCollection($app);
$controllers->get('/{token}', 'controller.media_accessor:showAction')
->bind('media_accessor');
return $controllers;
}
}

View File

@@ -13,7 +13,7 @@ use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Table(name="Secrets") * @ORM\Table(name="Secrets")
* @ORM\Entity() * @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\SecretRepository")
*/ */
class Secret class Secret
{ {

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Provider;
use Alchemy\Phrasea\Model\Entities\Secret;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Repositories\SecretRepository;
use RandomLib\Generator;
class DefaultSecretProvider implements SecretProvider
{
/** @var SecretRepository */
private $repository;
/** @var Generator */
private $generator;
public function __construct(SecretRepository $repository, Generator $generator)
{
$this->repository = $repository;
$this->generator = $generator;
}
public function getSecretForUser(User $user)
{
$secret = $this->repository->findOneBy(['creator' => $user], ['created' => 'DESC']);
if ($secret) {
return $secret;
}
$token = $this->generator->generateString(64, Generator::CHAR_ALNUM | Generator::CHAR_SYMBOLS);
$secret = new Secret($user, $token);
$this->repository->save($secret);
return $secret;
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Provider;
use Alchemy\Phrasea\Model\Entities\Secret;
use Alchemy\Phrasea\Model\Entities\User;
interface SecretProvider
{
/**
* Get a secret for a user.
* @param User $user
* @return Secret
*/
public function getSecretForUser(User $user);
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\Secret;
use Doctrine\ORM\EntityRepository;
/**
* This repository implements ArrayAccess to be used with php-jwt library
*/
class SecretRepository extends EntityRepository implements \ArrayAccess
{
public function offsetExists($offset)
{
return null !== $this->find($offset);
}
public function offsetGet($offset)
{
return $this->find($offset);
}
public function offsetSet($offset, $value)
{
throw new \LogicException('This ArrayAccess is non mutable.');
}
public function offsetUnset($offset)
{
throw new \LogicException('This ArrayAccess is non mutable.');
}
public function save(Secret $secret)
{
$this->_em->persist($secret);
$this->_em->flush($secret);
}
}