aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/api
diff options
context:
space:
mode:
Diffstat (limited to 'application/api')
-rw-r--r--application/api/ApiMiddleware.php21
-rw-r--r--application/api/ApiUtils.php17
-rw-r--r--application/api/controllers/ApiController.php3
-rw-r--r--application/api/controllers/Links.php23
4 files changed, 46 insertions, 18 deletions
diff --git a/application/api/ApiMiddleware.php b/application/api/ApiMiddleware.php
index 4745ac94..adc8b266 100644
--- a/application/api/ApiMiddleware.php
+++ b/application/api/ApiMiddleware.php
@@ -1,6 +1,7 @@
1<?php 1<?php
2namespace Shaarli\Api; 2namespace Shaarli\Api;
3 3
4use malkusch\lock\mutex\FlockMutex;
4use Shaarli\Api\Exceptions\ApiAuthorizationException; 5use Shaarli\Api\Exceptions\ApiAuthorizationException;
5use Shaarli\Api\Exceptions\ApiException; 6use Shaarli\Api\Exceptions\ApiException;
6use Shaarli\Bookmark\BookmarkFileService; 7use Shaarli\Bookmark\BookmarkFileService;
@@ -71,7 +72,14 @@ class ApiMiddleware
71 $response = $e->getApiResponse(); 72 $response = $e->getApiResponse();
72 } 73 }
73 74
74 return $response; 75 return $response
76 ->withHeader('Access-Control-Allow-Origin', '*')
77 ->withHeader(
78 'Access-Control-Allow-Headers',
79 'X-Requested-With, Content-Type, Accept, Origin, Authorization'
80 )
81 ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
82 ;
75 } 83 }
76 84
77 /** 85 /**
@@ -100,7 +108,9 @@ class ApiMiddleware
100 */ 108 */
101 protected function checkToken($request) 109 protected function checkToken($request)
102 { 110 {
103 if (! $request->hasHeader('Authorization')) { 111 if (!$request->hasHeader('Authorization')
112 && !isset($this->container->environment['REDIRECT_HTTP_AUTHORIZATION'])
113 ) {
104 throw new ApiAuthorizationException('JWT token not provided'); 114 throw new ApiAuthorizationException('JWT token not provided');
105 } 115 }
106 116
@@ -108,7 +118,11 @@ class ApiMiddleware
108 throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration'); 118 throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration');
109 } 119 }
110 120
111 $authorization = $request->getHeaderLine('Authorization'); 121 if (isset($this->container->environment['REDIRECT_HTTP_AUTHORIZATION'])) {
122 $authorization = $this->container->environment['REDIRECT_HTTP_AUTHORIZATION'];
123 } else {
124 $authorization = $request->getHeaderLine('Authorization');
125 }
112 126
113 if (! preg_match('/^Bearer (.*)/i', $authorization, $matches)) { 127 if (! preg_match('/^Bearer (.*)/i', $authorization, $matches)) {
114 throw new ApiAuthorizationException('Invalid JWT header'); 128 throw new ApiAuthorizationException('Invalid JWT header');
@@ -130,6 +144,7 @@ class ApiMiddleware
130 $linkDb = new BookmarkFileService( 144 $linkDb = new BookmarkFileService(
131 $conf, 145 $conf,
132 $this->container->get('history'), 146 $this->container->get('history'),
147 new FlockMutex(fopen(SHAARLI_MUTEX_FILE, 'r'), 2),
133 true 148 true
134 ); 149 );
135 $this->container['db'] = $linkDb; 150 $this->container['db'] = $linkDb;
diff --git a/application/api/ApiUtils.php b/application/api/ApiUtils.php
index 5156a5f7..eb1ca9bc 100644
--- a/application/api/ApiUtils.php
+++ b/application/api/ApiUtils.php
@@ -67,7 +67,7 @@ class ApiUtils
67 if (! $bookmark->isNote()) { 67 if (! $bookmark->isNote()) {
68 $out['url'] = $bookmark->getUrl(); 68 $out['url'] = $bookmark->getUrl();
69 } else { 69 } else {
70 $out['url'] = $indexUrl . $bookmark->getUrl(); 70 $out['url'] = rtrim($indexUrl, '/') . '/' . ltrim($bookmark->getUrl(), '/');
71 } 71 }
72 $out['shorturl'] = $bookmark->getShortUrl(); 72 $out['shorturl'] = $bookmark->getShortUrl();
73 $out['title'] = $bookmark->getTitle(); 73 $out['title'] = $bookmark->getTitle();
@@ -89,12 +89,12 @@ class ApiUtils
89 * If no URL is provided, it will generate a local note URL. 89 * If no URL is provided, it will generate a local note URL.
90 * If no title is provided, it will use the URL as title. 90 * If no title is provided, it will use the URL as title.
91 * 91 *
92 * @param array $input Request Link. 92 * @param array|null $input Request Link.
93 * @param bool $defaultPrivate Request Link. 93 * @param bool $defaultPrivate Setting defined if a bookmark is private by default.
94 * 94 *
95 * @return Bookmark instance. 95 * @return Bookmark instance.
96 */ 96 */
97 public static function buildLinkFromRequest($input, $defaultPrivate) 97 public static function buildBookmarkFromRequest(?array $input, bool $defaultPrivate): Bookmark
98 { 98 {
99 $bookmark = new Bookmark(); 99 $bookmark = new Bookmark();
100 $url = ! empty($input['url']) ? cleanup_url($input['url']) : ''; 100 $url = ! empty($input['url']) ? cleanup_url($input['url']) : '';
@@ -110,6 +110,15 @@ class ApiUtils
110 $bookmark->setTags(! empty($input['tags']) ? $input['tags'] : []); 110 $bookmark->setTags(! empty($input['tags']) ? $input['tags'] : []);
111 $bookmark->setPrivate($private); 111 $bookmark->setPrivate($private);
112 112
113 $created = \DateTime::createFromFormat(\DateTime::ATOM, $input['created'] ?? '');
114 if ($created instanceof \DateTimeInterface) {
115 $bookmark->setCreated($created);
116 }
117 $updated = \DateTime::createFromFormat(\DateTime::ATOM, $input['updated'] ?? '');
118 if ($updated instanceof \DateTimeInterface) {
119 $bookmark->setUpdated($updated);
120 }
121
113 return $bookmark; 122 return $bookmark;
114 } 123 }
115 124
diff --git a/application/api/controllers/ApiController.php b/application/api/controllers/ApiController.php
index c4b3d0c3..88a845eb 100644
--- a/application/api/controllers/ApiController.php
+++ b/application/api/controllers/ApiController.php
@@ -4,6 +4,7 @@ namespace Shaarli\Api\Controllers;
4 4
5use Shaarli\Bookmark\BookmarkServiceInterface; 5use Shaarli\Bookmark\BookmarkServiceInterface;
6use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
7use Shaarli\History;
7use Slim\Container; 8use Slim\Container;
8 9
9/** 10/**
@@ -31,7 +32,7 @@ abstract class ApiController
31 protected $bookmarkService; 32 protected $bookmarkService;
32 33
33 /** 34 /**
34 * @var HistoryController 35 * @var History
35 */ 36 */
36 protected $history; 37 protected $history;
37 38
diff --git a/application/api/controllers/Links.php b/application/api/controllers/Links.php
index 16fc8688..6bf529e4 100644
--- a/application/api/controllers/Links.php
+++ b/application/api/controllers/Links.php
@@ -96,11 +96,12 @@ class Links extends ApiController
96 */ 96 */
97 public function getLink($request, $response, $args) 97 public function getLink($request, $response, $args)
98 { 98 {
99 if (!$this->bookmarkService->exists($args['id'])) { 99 $id = is_integer_mixed($args['id']) ? (int) $args['id'] : null;
100 if ($id === null || ! $this->bookmarkService->exists($id)) {
100 throw new ApiLinkNotFoundException(); 101 throw new ApiLinkNotFoundException();
101 } 102 }
102 $index = index_url($this->ci['environment']); 103 $index = index_url($this->ci['environment']);
103 $out = ApiUtils::formatLink($this->bookmarkService->get($args['id']), $index); 104 $out = ApiUtils::formatLink($this->bookmarkService->get($id), $index);
104 105
105 return $response->withJson($out, 200, $this->jsonStyle); 106 return $response->withJson($out, 200, $this->jsonStyle);
106 } 107 }
@@ -115,8 +116,8 @@ class Links extends ApiController
115 */ 116 */
116 public function postLink($request, $response) 117 public function postLink($request, $response)
117 { 118 {
118 $data = $request->getParsedBody(); 119 $data = (array) ($request->getParsedBody() ?? []);
119 $bookmark = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links')); 120 $bookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links'));
120 // duplicate by URL, return 409 Conflict 121 // duplicate by URL, return 409 Conflict
121 if (! empty($bookmark->getUrl()) 122 if (! empty($bookmark->getUrl())
122 && ! empty($dup = $this->bookmarkService->findByUrl($bookmark->getUrl())) 123 && ! empty($dup = $this->bookmarkService->findByUrl($bookmark->getUrl()))
@@ -148,18 +149,19 @@ class Links extends ApiController
148 */ 149 */
149 public function putLink($request, $response, $args) 150 public function putLink($request, $response, $args)
150 { 151 {
151 if (! $this->bookmarkService->exists($args['id'])) { 152 $id = is_integer_mixed($args['id']) ? (int) $args['id'] : null;
153 if ($id === null || !$this->bookmarkService->exists($id)) {
152 throw new ApiLinkNotFoundException(); 154 throw new ApiLinkNotFoundException();
153 } 155 }
154 156
155 $index = index_url($this->ci['environment']); 157 $index = index_url($this->ci['environment']);
156 $data = $request->getParsedBody(); 158 $data = $request->getParsedBody();
157 159
158 $requestBookmark = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links')); 160 $requestBookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links'));
159 // duplicate URL on a different link, return 409 Conflict 161 // duplicate URL on a different link, return 409 Conflict
160 if (! empty($requestBookmark->getUrl()) 162 if (! empty($requestBookmark->getUrl())
161 && ! empty($dup = $this->bookmarkService->findByUrl($requestBookmark->getUrl())) 163 && ! empty($dup = $this->bookmarkService->findByUrl($requestBookmark->getUrl()))
162 && $dup->getId() != $args['id'] 164 && $dup->getId() != $id
163 ) { 165 ) {
164 return $response->withJson( 166 return $response->withJson(
165 ApiUtils::formatLink($dup, $index), 167 ApiUtils::formatLink($dup, $index),
@@ -168,7 +170,7 @@ class Links extends ApiController
168 ); 170 );
169 } 171 }
170 172
171 $responseBookmark = $this->bookmarkService->get($args['id']); 173 $responseBookmark = $this->bookmarkService->get($id);
172 $responseBookmark = ApiUtils::updateLink($responseBookmark, $requestBookmark); 174 $responseBookmark = ApiUtils::updateLink($responseBookmark, $requestBookmark);
173 $this->bookmarkService->set($responseBookmark); 175 $this->bookmarkService->set($responseBookmark);
174 176
@@ -189,10 +191,11 @@ class Links extends ApiController
189 */ 191 */
190 public function deleteLink($request, $response, $args) 192 public function deleteLink($request, $response, $args)
191 { 193 {
192 if (! $this->bookmarkService->exists($args['id'])) { 194 $id = is_integer_mixed($args['id']) ? (int) $args['id'] : null;
195 if ($id === null || !$this->bookmarkService->exists($id)) {
193 throw new ApiLinkNotFoundException(); 196 throw new ApiLinkNotFoundException();
194 } 197 }
195 $bookmark = $this->bookmarkService->get($args['id']); 198 $bookmark = $this->bookmarkService->get($id);
196 $this->bookmarkService->remove($bookmark); 199 $this->bookmarkService->remove($bookmark);
197 200
198 return $response->withStatus(204); 201 return $response->withStatus(204);