diff --git a/app/config/security.yml b/app/config/security.yml index 93d9ded1..8eed60b2 100755 --- a/app/config/security.yml +++ b/app/config/security.yml @@ -93,6 +93,5 @@ security: - { path: ^/admin/, role: [ROLE_SONATA_ADMIN] } - { path: ^/user/, role: IS_AUTHENTICATED_REMEMBERED } - { path: ^/duplicates/, role: [ROLE_ADMIN] } - - { path: ^/api/$, role: IS_AUTHENTICATED_REMEMBERED } - { path: ^/api/.*, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY } \ No newline at end of file diff --git a/src/Biopen/CoreBundle/Admin/ConfigurationAdmin.php b/src/Biopen/CoreBundle/Admin/ConfigurationAdmin.php index 544b894a..13cda41c 100755 --- a/src/Biopen/CoreBundle/Admin/ConfigurationAdmin.php +++ b/src/Biopen/CoreBundle/Admin/ConfigurationAdmin.php @@ -62,6 +62,12 @@ protected function configureFormFields(FormMapper $formMapper) ->with("Entrez du code du code HTML (iframe par exemple) qui sera affichée sur la page d'accueil de l'interface admin") ->add('customDashboard', 'textarea', array('label' => 'Custom HTML code', 'attr' => ['rows' => '15'], 'required' => false)) ->end() + ->end() + ->tab('API') + ->with("Configurer les API (Utilisateurs avancés)") + ->add('api.protectPublicApiWithToken', 'checkbox', array('label' => "Protéger l'api publique avec des jetons utilisateurs (i.e. besoin de créer un compte pour utiliser l'api publique)", 'required' => false)) + ->add('api.internalApiAuthorizedDomains', 'text', array('label' => "Liste des domaines externe qui utiliseront l'API interne. Mettez * si vous voulez que n'importe quel domaine puisse y avoir accès. Cette option est nécessaire si vous voulez afficher vos données avec GoGoCartoJs mais sur un autre serveur.", 'required' => false)) + ->end() ->end(); } } \ No newline at end of file diff --git a/src/Biopen/CoreBundle/Document/Configuration.php b/src/Biopen/CoreBundle/Document/Configuration.php index 151c8ff2..69931113 100644 --- a/src/Biopen/CoreBundle/Document/Configuration.php +++ b/src/Biopen/CoreBundle/Document/Configuration.php @@ -8,7 +8,7 @@ use Biopen\CoreBundle\Document\Configuration\ConfigurationUser; use Biopen\CoreBundle\Document\Configuration\ConfigurationMenu; use Biopen\CoreBundle\Document\Configuration\ConfigurationInfobar; - +use Biopen\CoreBundle\Document\Configuration\ConfigurationApi; /** * Main Configuration @@ -403,6 +403,13 @@ class Configuration /** @MongoDB\Field(type="string") */ protected $customDashboard = ''; + // ------------------------- + // --------- API ----------- + // ------------------------- + + /** @MongoDB\EmbedOne(targetDocument="Biopen\CoreBundle\Document\Configuration\ConfigurationApi") */ + protected $api; + public function __toString() { @@ -444,6 +451,7 @@ public function __construct() $this->user = new ConfigurationUser(); $this->menu = new ConfigurationMenu(); $this->infobar = new ConfigurationInfobar(); + $this->api = new ConfigurationApi(); } /** @@ -2816,4 +2824,27 @@ public function getSearchElementsFeature() { return $this->searchElementsFeature; } + + /** + * Set api + * + * @param Biopen\CoreBundle\Document\Configuration\ConfigurationApi $api + * @return $this + */ + public function setApi(\Biopen\CoreBundle\Document\Configuration\ConfigurationApi $api) + { + $this->api = $api; + return $this; + } + + /** + * Get api + * + * @return Biopen\CoreBundle\Document\Configuration\ConfigurationApi $api + */ + public function getApi() + { + if(!$this->api) $this->api = new ConfigurationApi(); + return $this->api; + } } diff --git a/src/Biopen/CoreBundle/Document/Configuration/ConfigurationApi.php b/src/Biopen/CoreBundle/Document/Configuration/ConfigurationApi.php new file mode 100644 index 00000000..317814b9 --- /dev/null +++ b/src/Biopen/CoreBundle/Document/Configuration/ConfigurationApi.php @@ -0,0 +1,59 @@ +internalApiAuthorizedDomains = $internalApiAuthorizedDomains; + return $this; + } + + /** + * Get internalApiAuthorizedDomains + * + * @return string $internalApiAuthorizedDomains + */ + public function getInternalApiAuthorizedDomains() + { + return $this->internalApiAuthorizedDomains; + } + + /** + * Set protectPublicApiWithToken + * + * @param bool $protectPublicApiWithToken + * @return $this + */ + public function setProtectPublicApiWithToken($protectPublicApiWithToken) + { + $this->protectPublicApiWithToken = $protectPublicApiWithToken; + return $this; + } + + /** + * Get protectPublicApiWithToken + * + * @return bool $protectPublicApiWithToken + */ + public function getProtectPublicApiWithToken() + { + return $this->protectPublicApiWithToken; + } +} diff --git a/src/Biopen/GeoDirectoryBundle/Controller/APIController.php b/src/Biopen/GeoDirectoryBundle/Controller/APIController.php index b4c47981..fcb40037 100755 --- a/src/Biopen/GeoDirectoryBundle/Controller/APIController.php +++ b/src/Biopen/GeoDirectoryBundle/Controller/APIController.php @@ -48,24 +48,34 @@ public function getElementsAction(Request $request, $id = null, $_format = 'json $token = $request->get('token'); $ontology = $request->get('ontology') ? strtolower($request->get('ontology')) : "gogofull"; $fullRepresentation = $jsonLdRequest || $ontology != "gogocompact"; - $elementId = $id ? $id : $request->get('id'); - + $elementId = $id ? $id : $request->get('id'); + $config = $em->getRepository('BiopenCoreBundle:Configuration')->findConfiguration(); + $protectWithToken = $config->getApi()->getProtectPublicApiWithToken(); + $apiUiUrl = $this->generateUrl('biopen_api_ui', [], UrlGeneratorInterface::ABSOLUTE_URL); + // allow ajax request from same host - if ($request->isXmlHttpRequest() && $this->requestFromSameHost($request)) + if ($request->isXmlHttpRequest()) { $isAdmin = $this->isUserAdmin(); - $includeContact = true; + $includePrivateFields = true; } - else if ($token) // otherwise API is protected by user token + else if (!$protectWithToken || $token) // otherwise API is protected by user token { - $user = $em->getRepository('BiopenCoreBundle:User')->findOneByToken($token); - if (!$user) return new Response("The token you provided does not correspond to any existing user. Please visit " . $this->generateUrl('biopen_api_ui', [], UrlGeneratorInterface::ABSOLUTE_URL)); + if ($protectWithToken) + { + $user = $em->getRepository('BiopenCoreBundle:User')->findOneByToken($token); + if (!$user) { + $response = "The token you provided does not correspond to any existing user. Please visit " . $apiUiUrl; + return $this->createResponse($response, $config); + } + } $isAdmin = false; - $includeContact = false; + $includePrivateFields = false; } else { - return new Response("You need to provide a token to access to this API. Please visit " . $this->generateUrl('biopen_api_ui', [], UrlGeneratorInterface::ABSOLUTE_URL)); + $response = "You need to provide a token to access to this API. Please visit " . $apiUiUrl; + return $this->createResponse($response, $config); } $elementRepo = $em->getRepository('BiopenGeoDirectoryBundle:Element'); @@ -73,7 +83,7 @@ public function getElementsAction(Request $request, $id = null, $_format = 'json if ($elementId) { $element = $elementRepo->findOneBy(array('id' => $elementId)); - $elementsJson = $element->getJson($includeContact, $isAdmin); + $elementsJson = $element->getJson($includePrivateFields, $isAdmin); } else { @@ -92,7 +102,7 @@ public function getElementsAction(Request $request, $id = null, $_format = 'json { $elementsFromDB = $elementRepo->findAllPublics($fullRepresentation, $isAdmin, $request); } - $elementsJson = $this->encodeElementArrayToJsonArray($elementsFromDB, $fullRepresentation, $isAdmin, $includeContact); + $elementsJson = $this->encodeElementArrayToJsonArray($elementsFromDB, $fullRepresentation, $isAdmin, $includePrivateFields); } if ($jsonLdRequest) @@ -114,10 +124,8 @@ public function getElementsAction(Request $request, $id = null, $_format = 'json // $responseSize = strlen($elementsJson); // $date = date('d/m/Y'); - $result = new Response($responseJson); - $result->headers->set('Content-Type', 'application/json'); - return $result; - } + return $this->createResponse($responseJson, $config); + } public function getTaxonomyAction(Request $request, $id = null, $_format = 'json') { @@ -138,7 +146,6 @@ public function getTaxonomyAction(Request $request, $id = null, $_format = 'json { $dataJson = $em->getRepository('BiopenGeoDirectoryBundle:Taxonomy')->findTaxonomyJson($jsonLdRequest); } - if ($jsonLdRequest) $responseJson = '{ @@ -148,9 +155,8 @@ public function getTaxonomyAction(Request $request, $id = null, $_format = 'json else $responseJson = $dataJson; - $response = new Response($responseJson); - $response->headers->set('Content-Type', 'application/json'); - return $response; + $config = $em->getRepository('BiopenCoreBundle:Configuration')->findConfiguration(); + return $this->createResponse($responseJson, $config); } private function isJsonLdRequest($request, $_format) @@ -158,10 +164,13 @@ private function isJsonLdRequest($request, $_format) return $_format == 'jsonld' || $request->headers->get('Accept') == 'application/ld+json'; } - private function requestFromSameHost($request) + private function createResponse($text, $config) { - if (!(isset($_SERVER['HTTP_REFERER']) || empty($_SERVER['HTTP_REFERER']))) return false; - return strtolower(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) == strtolower($_SERVER['HTTP_HOST']); + $response = new Response($text); + if ($config->getApi()->getInternalApiAuthorizedDomains()) + $response->headers->set('Access-Control-Allow-Origin', $config->getApi()->getInternalApiAuthorizedDomains()); + $response->headers->set('Content-Type', 'application/json'); + return $response; } public function getElementsFromTextAction(Request $request) @@ -172,42 +181,43 @@ public function getElementsFromTextAction(Request $request) $isAdmin = $this->isUserAdmin(); - $elements = $em->getRepository('BiopenGeoDirectoryBundle:Element') - ->findElementsWithText($request->get('text'), true, $isAdmin); - - // $elements = array_filter($elements, function($value) { - // return (float) $value['score'] >= 0; - // }); + $elements = $em->getRepository('BiopenGeoDirectoryBundle:Element')->findElementsWithText($request->get('text'), true, $isAdmin); $elementsJson = $this->encodeElementArrayToJsonArray($elements, true, $isAdmin, true); $responseJson = '{ "data":'. $elementsJson . ', "ontology" : "gogofull"}'; - $response = new Response($responseJson); - $response->headers->set('Content-Type', 'application/json'); - return $response; - } - else - { - return new Response("Access to the API is restricted and not allowed via the browser"); + $config = $em->getRepository('BiopenCoreBundle:Configuration')->findConfiguration(); + return $this->createResponse($responseJson, $config); } + else { return new Response("Access to the API is restricted and not allowed via the browser"); } } public function apiUiAction() { $em = $this->get('doctrine_mongodb')->getManager(); - $options = $em->getRepository('BiopenGeoDirectoryBundle:Option')->findAll(); + $config = $em->getRepository('BiopenCoreBundle:Configuration')->findConfiguration(); + $protectPublicApiWithToken = $config->getApi()->getProtectPublicApiWithToken(); + + $securityContext = $this->get('security.context'); + $userLoggued = $securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED'); - $user = $this->get('security.context')->getToken()->getUser(); - if (!$user->getToken()) + if ($protectPublicApiWithToken && !$userLoggued) { + $this->getRequest()->getSession()->set('_security.main.target_path', 'api'); + return $this->redirectToRoute('fos_user_security_login'); + } + + if ($protectPublicApiWithToken) { - $user->createToken(); - $em->flush(); + $user = $securityContext->getToken()->getUser(); + if (!$user->getToken()) { $user->createToken(); $em->flush(); } } + $options = $em->getRepository('BiopenGeoDirectoryBundle:Option')->findAll(); return $this->render('BiopenGeoDirectoryBundle:api:api-ui.html.twig', array('options' => $options)); } - public function getManifestAction() { + public function getManifestAction() + { $em = $this->get('doctrine_mongodb')->getManager(); $config = $em->getRepository('BiopenCoreBundle:Configuration')->findConfiguration(); $img = $config->getFavicon() ? $config->getFavicon() : $config->getLogo(); @@ -229,9 +239,9 @@ public function getManifestAction() { "background_color" => $config->getBackgroundColor(), "icons" => [ [ - "src" => $imgUrl, - "sizes" => $imageData->height().'x'.$imageData->width(), - "type" => $imageData->mime() + "src" => $imgUrl, + "sizes" => $imageData->height().'x'.$imageData->width(), + "type" => $imageData->mime() ] ] ); @@ -249,11 +259,10 @@ private function isUserAdmin() $isAdmin = $user && $user->isAdmin(); return $isAdmin; } - return false; - + return false; } - private function encodeElementArrayToJsonArray($array, $fullRepresentation, $isAdmin = false, $includeContact = false) + private function encodeElementArrayToJsonArray($array, $fullRepresentation, $isAdmin = false, $includePrivateFields = false) { $elementsJson = '['; foreach ($array as $key => $value) @@ -261,7 +270,7 @@ private function encodeElementArrayToJsonArray($array, $fullRepresentation, $isA if ($fullRepresentation == 'true') { $elementJson = $value['baseJson']; - if ($includeContact && $value['privateJson'] != '{}') { + if ($includePrivateFields && $value['privateJson'] != '{}') { $elementJson = substr($elementJson , 0, -1) . ',' . substr($value['privateJson'],1); } if ($isAdmin && $value['adminJson'] != '{}') { diff --git a/src/Biopen/GeoDirectoryBundle/Document/Element.php b/src/Biopen/GeoDirectoryBundle/Document/Element.php index 7c6b3182..6e6a6495 100644 --- a/src/Biopen/GeoDirectoryBundle/Document/Element.php +++ b/src/Biopen/GeoDirectoryBundle/Document/Element.php @@ -465,21 +465,23 @@ public function updateJsonRepresentation() if (!$this->geo) { return; } // -------------------- FULL JSON ---------------- + + // BASIC FIELDS $baseJson = json_encode($this); $baseJson = substr($baseJson , 0, -1); // remove last '}' - - if ($this->address) $baseJson .= ', "address":' . $this->address->toJson(); - if ($this->address) $baseJson .= ', "email":' . json_encode($this->email); + if ($this->address) $baseJson .= ', "address":' . $this->address->toJson(); if ($this->openHours) $baseJson .= ', "openHours": ' . $this->openHours->toJson(); + // CREATED AT, UPDATED AT $baseJson .= ', "createdAt":"' . date_format($this->createdAt,"d/m/Y") . '"'; $updatedAt = $this->updatedAt ? $this->updatedAt : $this->createdAt; $updatedAtFormated = gettype($updatedAt) == "integer" ? date("d/m/Y", $updatedAt) : date_format($updatedAt,"d/m/Y"); $baseJson .= ', "updatedAt":"' . $updatedAtFormated . '"'; + // OPTIONS VALUES (= TAXONOMY) $sortedOptionsValues = $this->getSortedOptionsValues(); $optValuesLength = count($sortedOptionsValues); - // OPTIONS VALUES IDS + // Options values ids $baseJson .= ', "categories": ['; if ($sortedOptionsValues) { @@ -489,17 +491,7 @@ public function updateJsonRepresentation() } $baseJson = rtrim($baseJson, ','); $baseJson .= '],'; - - foreach ($this->getData() as $key => $value) { - $baseJson .= '"'. $key .'": ' . json_encode($value) . ','; - } - - $baseJson .= $this->encodeArrayObjectToJson("stamps", $this->stamps); - $baseJson .= $this->encodeArrayObjectToJson("images", $this->images); - - $baseJson = rtrim($baseJson, ','); - - // OPTIONS VALUES WITH DESCRIPTIONS + // Options values with descriptionO $optionDescriptionsJson = []; if ($sortedOptionsValues) { @@ -507,8 +499,19 @@ public function updateJsonRepresentation() if ($sortedOptionsValues[$i]->getDescription()) $optionDescriptionsJson[] = $sortedOptionsValues[$i]->toJson(); } } - if (count($optionDescriptionsJson)) $baseJson .= ', "categoriesDescriptions": [' . implode(",", $optionDescriptionsJson) . ']'; + if (count($optionDescriptionsJson)) $baseJson .= '"categoriesDescriptions": [' . implode(",", $optionDescriptionsJson) . '],'; + // CUSTOM DATA + foreach ($this->getData() as $key => $value) { + $baseJson .= '"'. $key .'": ' . json_encode($value) . ','; + } + + // SPECIFIC DATA + $baseJson .= $this->encodeArrayObjectToJson("stamps", $this->stamps); + $baseJson .= $this->encodeArrayObjectToJson("images", $this->images); + $baseJson = rtrim($baseJson, ','); + + // MODIFIED ELEMENT (for pending modification) if ($this->getModifiedElement()) { $baseJson .= ', "modifiedElement": ' . $this->getModifiedElement()->getJson(true, false); } @@ -517,17 +520,18 @@ public function updateJsonRepresentation() $this->setBaseJson($baseJson); - // -------------------- PRIVATE ------------------------- + // -------------------- PRIVATE JSON ------------------------- $privateJson = '{'; // status $privateJson .= '"status": ' . $this->getStatus() . ','; + if ($this->email) $privateJson .= '"email":' . json_encode($this->email) . ','; $privateJson .= '"moderationState": ' . $this->getModerationState() . ','; $privateJson = rtrim($privateJson, ','); $privateJson .= '}'; $this->setPrivateJson($privateJson); - // -------------------- REPORTS & CONTRIBUTIONS ------------------------- + // ---------------- ADMIN JSON = REPORTS & CONTRIBUTIONS --------------------- $adminJson = '{'; if ($this->status != ElementStatus::ModifiedPendingVersion) { @@ -540,7 +544,8 @@ public function updateJsonRepresentation() $adminJson = rtrim($adminJson, ','); } $adminJson .= '}'; - $this->setAdminJson($adminJson); + $this->setAdminJson($adminJson); + // -------------------- COMPACT JSON ---------------- $compactJson = '["'.$this->id . '",' . json_encode($this->name) . ','; @@ -561,12 +566,12 @@ public function updateJsonRepresentation() $this->setCompactJson($compactJson); } - public function getJson($includeContact, $isAdmin) + public function getJson($includePrivateJson, $includeAdminJson) { $result = $this->baseJson; - if ($includeContact && $this->privateJson && $this->privateJson != '{}') + if ($includePrivateJson && $this->privateJson && $this->privateJson != '{}') $result = substr($result , 0, -1) . ',' . substr($this->privateJson,1); - if ($isAdmin && $this->adminJson && $this->adminJson != '{}') + if ($includeAdminJson && $this->adminJson && $this->adminJson != '{}') $result = substr($result , 0, -1) . ',' . substr($this->adminJson,1); return $result; } diff --git a/src/Biopen/GeoDirectoryBundle/Resources/js/api/api.js b/src/Biopen/GeoDirectoryBundle/Resources/js/api/api.js index 6b65a3b5..60ac5120 100755 --- a/src/Biopen/GeoDirectoryBundle/Resources/js/api/api.js +++ b/src/Biopen/GeoDirectoryBundle/Resources/js/api/api.js @@ -60,7 +60,7 @@ if ($('#page-content.api').length > 0) if ($('#use-limit').is(':checked') && $('#limit-input').val() > 0) params.limit = $('#limit-input').val(); if ($('#use-categories').is(':checked') && $('.select-categories').val()) params.categories = $('.select-categories').val(); if ($('#use-bounds').is(':checked') && bounds) params.bounds = bounds; - params.token = userToken; + if (userToken) params.token = userToken; url += '.' + $('input[name=format]:checked').data('value'); var encodedParams = encodeQueryData(params); diff --git a/src/Biopen/GeoDirectoryBundle/Resources/views/api/api-ui.html.twig b/src/Biopen/GeoDirectoryBundle/Resources/views/api/api-ui.html.twig index 80197ac3..85530d41 100755 --- a/src/Biopen/GeoDirectoryBundle/Resources/views/api/api-ui.html.twig +++ b/src/Biopen/GeoDirectoryBundle/Resources/views/api/api-ui.html.twig @@ -76,7 +76,7 @@ var defaultBounds = {{ config.getDefaultBounds | json_encode | raw }} var apiUrlBase = "{{ url('biopen_api_elements_index') }}"; - var userToken = "{{ app.user.token }}"; + var userToken = "{{ config.api.protectPublicApiWithToken ? app.user.token : null }}"; {% endblock %}