diff options
Diffstat (limited to 'application/api')
-rw-r--r-- | application/api/ApiMiddleware.php | 4 | ||||
-rw-r--r-- | application/api/ApiUtils.php | 27 | ||||
-rw-r--r-- | application/api/controllers/HistoryController.php | 1 | ||||
-rw-r--r-- | application/api/controllers/Info.php | 4 | ||||
-rw-r--r-- | application/api/controllers/Links.php | 20 | ||||
-rw-r--r-- | application/api/exceptions/ApiAuthorizationException.php | 2 | ||||
-rw-r--r-- | application/api/exceptions/ApiException.php | 2 |
7 files changed, 43 insertions, 17 deletions
diff --git a/application/api/ApiMiddleware.php b/application/api/ApiMiddleware.php index adc8b266..9fb88358 100644 --- a/application/api/ApiMiddleware.php +++ b/application/api/ApiMiddleware.php | |||
@@ -1,4 +1,5 @@ | |||
1 | <?php | 1 | <?php |
2 | |||
2 | namespace Shaarli\Api; | 3 | namespace Shaarli\Api; |
3 | 4 | ||
4 | use malkusch\lock\mutex\FlockMutex; | 5 | use malkusch\lock\mutex\FlockMutex; |
@@ -108,7 +109,8 @@ class ApiMiddleware | |||
108 | */ | 109 | */ |
109 | protected function checkToken($request) | 110 | protected function checkToken($request) |
110 | { | 111 | { |
111 | if (!$request->hasHeader('Authorization') | 112 | if ( |
113 | !$request->hasHeader('Authorization') | ||
112 | && !isset($this->container->environment['REDIRECT_HTTP_AUTHORIZATION']) | 114 | && !isset($this->container->environment['REDIRECT_HTTP_AUTHORIZATION']) |
113 | ) { | 115 | ) { |
114 | throw new ApiAuthorizationException('JWT token not provided'); | 116 | throw new ApiAuthorizationException('JWT token not provided'); |
diff --git a/application/api/ApiUtils.php b/application/api/ApiUtils.php index eb1ca9bc..9228bb2d 100644 --- a/application/api/ApiUtils.php +++ b/application/api/ApiUtils.php | |||
@@ -1,4 +1,5 @@ | |||
1 | <?php | 1 | <?php |
2 | |||
2 | namespace Shaarli\Api; | 3 | namespace Shaarli\Api; |
3 | 4 | ||
4 | use Shaarli\Api\Exceptions\ApiAuthorizationException; | 5 | use Shaarli\Api\Exceptions\ApiAuthorizationException; |
@@ -27,7 +28,7 @@ class ApiUtils | |||
27 | throw new ApiAuthorizationException('Malformed JWT token'); | 28 | throw new ApiAuthorizationException('Malformed JWT token'); |
28 | } | 29 | } |
29 | 30 | ||
30 | $genSign = Base64Url::encode(hash_hmac('sha512', $parts[0] .'.'. $parts[1], $secret, true)); | 31 | $genSign = Base64Url::encode(hash_hmac('sha512', $parts[0] . '.' . $parts[1], $secret, true)); |
31 | if ($parts[2] != $genSign) { | 32 | if ($parts[2] != $genSign) { |
32 | throw new ApiAuthorizationException('Invalid JWT signature'); | 33 | throw new ApiAuthorizationException('Invalid JWT signature'); |
33 | } | 34 | } |
@@ -42,7 +43,8 @@ class ApiUtils | |||
42 | throw new ApiAuthorizationException('Invalid JWT payload'); | 43 | throw new ApiAuthorizationException('Invalid JWT payload'); |
43 | } | 44 | } |
44 | 45 | ||
45 | if (empty($payload->iat) | 46 | if ( |
47 | empty($payload->iat) | ||
46 | || $payload->iat > time() | 48 | || $payload->iat > time() |
47 | || time() - $payload->iat > ApiMiddleware::$TOKEN_DURATION | 49 | || time() - $payload->iat > ApiMiddleware::$TOKEN_DURATION |
48 | ) { | 50 | ) { |
@@ -89,13 +91,17 @@ class ApiUtils | |||
89 | * If no URL is provided, it will generate a local note URL. | 91 | * 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. | 92 | * If no title is provided, it will use the URL as title. |
91 | * | 93 | * |
92 | * @param array|null $input Request Link. | 94 | * @param array|null $input Request Link. |
93 | * @param bool $defaultPrivate Setting defined if a bookmark is private by default. | 95 | * @param bool $defaultPrivate Setting defined if a bookmark is private by default. |
96 | * @param string $tagsSeparator Tags separator loaded from the config file. | ||
94 | * | 97 | * |
95 | * @return Bookmark instance. | 98 | * @return Bookmark instance. |
96 | */ | 99 | */ |
97 | public static function buildBookmarkFromRequest(?array $input, bool $defaultPrivate): Bookmark | 100 | public static function buildBookmarkFromRequest( |
98 | { | 101 | ?array $input, |
102 | bool $defaultPrivate, | ||
103 | string $tagsSeparator | ||
104 | ): Bookmark { | ||
99 | $bookmark = new Bookmark(); | 105 | $bookmark = new Bookmark(); |
100 | $url = ! empty($input['url']) ? cleanup_url($input['url']) : ''; | 106 | $url = ! empty($input['url']) ? cleanup_url($input['url']) : ''; |
101 | if (isset($input['private'])) { | 107 | if (isset($input['private'])) { |
@@ -107,6 +113,15 @@ class ApiUtils | |||
107 | $bookmark->setTitle(! empty($input['title']) ? $input['title'] : ''); | 113 | $bookmark->setTitle(! empty($input['title']) ? $input['title'] : ''); |
108 | $bookmark->setUrl($url); | 114 | $bookmark->setUrl($url); |
109 | $bookmark->setDescription(! empty($input['description']) ? $input['description'] : ''); | 115 | $bookmark->setDescription(! empty($input['description']) ? $input['description'] : ''); |
116 | |||
117 | // Be permissive with provided tags format | ||
118 | if (is_string($input['tags'] ?? null)) { | ||
119 | $input['tags'] = tags_str2array($input['tags'], $tagsSeparator); | ||
120 | } | ||
121 | if (is_array($input['tags'] ?? null) && count($input['tags']) === 1 && is_string($input['tags'][0])) { | ||
122 | $input['tags'] = tags_str2array($input['tags'][0], $tagsSeparator); | ||
123 | } | ||
124 | |||
110 | $bookmark->setTags(! empty($input['tags']) ? $input['tags'] : []); | 125 | $bookmark->setTags(! empty($input['tags']) ? $input['tags'] : []); |
111 | $bookmark->setPrivate($private); | 126 | $bookmark->setPrivate($private); |
112 | 127 | ||
diff --git a/application/api/controllers/HistoryController.php b/application/api/controllers/HistoryController.php index 505647a9..d83a3a25 100644 --- a/application/api/controllers/HistoryController.php +++ b/application/api/controllers/HistoryController.php | |||
@@ -1,6 +1,5 @@ | |||
1 | <?php | 1 | <?php |
2 | 2 | ||
3 | |||
4 | namespace Shaarli\Api\Controllers; | 3 | namespace Shaarli\Api\Controllers; |
5 | 4 | ||
6 | use Shaarli\Api\Exceptions\ApiBadParametersException; | 5 | use Shaarli\Api\Exceptions\ApiBadParametersException; |
diff --git a/application/api/controllers/Info.php b/application/api/controllers/Info.php index 12f6b2f0..ae7db93e 100644 --- a/application/api/controllers/Info.php +++ b/application/api/controllers/Info.php | |||
@@ -29,13 +29,13 @@ class Info extends ApiController | |||
29 | $info = [ | 29 | $info = [ |
30 | 'global_counter' => $this->bookmarkService->count(), | 30 | 'global_counter' => $this->bookmarkService->count(), |
31 | 'private_counter' => $this->bookmarkService->count(BookmarkFilter::$PRIVATE), | 31 | 'private_counter' => $this->bookmarkService->count(BookmarkFilter::$PRIVATE), |
32 | 'settings' => array( | 32 | 'settings' => [ |
33 | 'title' => $this->conf->get('general.title', 'Shaarli'), | 33 | 'title' => $this->conf->get('general.title', 'Shaarli'), |
34 | 'header_link' => $this->conf->get('general.header_link', '?'), | 34 | 'header_link' => $this->conf->get('general.header_link', '?'), |
35 | 'timezone' => $this->conf->get('general.timezone', 'UTC'), | 35 | 'timezone' => $this->conf->get('general.timezone', 'UTC'), |
36 | 'enabled_plugins' => $this->conf->get('general.enabled_plugins', []), | 36 | 'enabled_plugins' => $this->conf->get('general.enabled_plugins', []), |
37 | 'default_private_links' => $this->conf->get('privacy.default_private_links', false), | 37 | 'default_private_links' => $this->conf->get('privacy.default_private_links', false), |
38 | ), | 38 | ], |
39 | ]; | 39 | ]; |
40 | 40 | ||
41 | return $response->withJson($info, 200, $this->jsonStyle); | 41 | return $response->withJson($info, 200, $this->jsonStyle); |
diff --git a/application/api/controllers/Links.php b/application/api/controllers/Links.php index 73a1b84e..b83b2260 100644 --- a/application/api/controllers/Links.php +++ b/application/api/controllers/Links.php | |||
@@ -117,9 +117,14 @@ class Links extends ApiController | |||
117 | public function postLink($request, $response) | 117 | public function postLink($request, $response) |
118 | { | 118 | { |
119 | $data = (array) ($request->getParsedBody() ?? []); | 119 | $data = (array) ($request->getParsedBody() ?? []); |
120 | $bookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links')); | 120 | $bookmark = ApiUtils::buildBookmarkFromRequest( |
121 | $data, | ||
122 | $this->conf->get('privacy.default_private_links'), | ||
123 | $this->conf->get('general.tags_separator', ' ') | ||
124 | ); | ||
121 | // duplicate by URL, return 409 Conflict | 125 | // duplicate by URL, return 409 Conflict |
122 | if (! empty($bookmark->getUrl()) | 126 | if ( |
127 | ! empty($bookmark->getUrl()) | ||
123 | && ! empty($dup = $this->bookmarkService->findByUrl($bookmark->getUrl())) | 128 | && ! empty($dup = $this->bookmarkService->findByUrl($bookmark->getUrl())) |
124 | ) { | 129 | ) { |
125 | return $response->withJson( | 130 | return $response->withJson( |
@@ -131,7 +136,7 @@ class Links extends ApiController | |||
131 | 136 | ||
132 | $this->bookmarkService->add($bookmark); | 137 | $this->bookmarkService->add($bookmark); |
133 | $out = ApiUtils::formatLink($bookmark, index_url($this->ci['environment'])); | 138 | $out = ApiUtils::formatLink($bookmark, index_url($this->ci['environment'])); |
134 | $redirect = $this->ci->router->relativePathFor('getLink', ['id' => $bookmark->getId()]); | 139 | $redirect = $this->ci->router->pathFor('getLink', ['id' => $bookmark->getId()]); |
135 | return $response->withAddedHeader('Location', $redirect) | 140 | return $response->withAddedHeader('Location', $redirect) |
136 | ->withJson($out, 201, $this->jsonStyle); | 141 | ->withJson($out, 201, $this->jsonStyle); |
137 | } | 142 | } |
@@ -157,9 +162,14 @@ class Links extends ApiController | |||
157 | $index = index_url($this->ci['environment']); | 162 | $index = index_url($this->ci['environment']); |
158 | $data = $request->getParsedBody(); | 163 | $data = $request->getParsedBody(); |
159 | 164 | ||
160 | $requestBookmark = ApiUtils::buildBookmarkFromRequest($data, $this->conf->get('privacy.default_private_links')); | 165 | $requestBookmark = ApiUtils::buildBookmarkFromRequest( |
166 | $data, | ||
167 | $this->conf->get('privacy.default_private_links'), | ||
168 | $this->conf->get('general.tags_separator', ' ') | ||
169 | ); | ||
161 | // duplicate URL on a different link, return 409 Conflict | 170 | // duplicate URL on a different link, return 409 Conflict |
162 | if (! empty($requestBookmark->getUrl()) | 171 | if ( |
172 | ! empty($requestBookmark->getUrl()) | ||
163 | && ! empty($dup = $this->bookmarkService->findByUrl($requestBookmark->getUrl())) | 173 | && ! empty($dup = $this->bookmarkService->findByUrl($requestBookmark->getUrl())) |
164 | && $dup->getId() != $id | 174 | && $dup->getId() != $id |
165 | ) { | 175 | ) { |
diff --git a/application/api/exceptions/ApiAuthorizationException.php b/application/api/exceptions/ApiAuthorizationException.php index 0e3f4776..c77e9eea 100644 --- a/application/api/exceptions/ApiAuthorizationException.php +++ b/application/api/exceptions/ApiAuthorizationException.php | |||
@@ -28,7 +28,7 @@ class ApiAuthorizationException extends ApiException | |||
28 | */ | 28 | */ |
29 | public function setMessage($message) | 29 | public function setMessage($message) |
30 | { | 30 | { |
31 | $original = $this->debug === true ? ': '. $this->getMessage() : ''; | 31 | $original = $this->debug === true ? ': ' . $this->getMessage() : ''; |
32 | $this->message = $message . $original; | 32 | $this->message = $message . $original; |
33 | } | 33 | } |
34 | } | 34 | } |
diff --git a/application/api/exceptions/ApiException.php b/application/api/exceptions/ApiException.php index d6b66323..7deafb96 100644 --- a/application/api/exceptions/ApiException.php +++ b/application/api/exceptions/ApiException.php | |||
@@ -44,7 +44,7 @@ abstract class ApiException extends \Exception | |||
44 | } | 44 | } |
45 | return [ | 45 | return [ |
46 | 'message' => $this->getMessage(), | 46 | 'message' => $this->getMessage(), |
47 | 'stacktrace' => get_class($this) .': '. $this->getTraceAsString() | 47 | 'stacktrace' => get_class($this) . ': ' . $this->getTraceAsString() |
48 | ]; | 48 | ]; |
49 | } | 49 | } |
50 | 50 | ||