]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - application/api/ApiUtils.php
Run Unit Tests against PHP 7.4
[github/shaarli/Shaarli.git] / application / api / ApiUtils.php
1 <?php
2 namespace Shaarli\Api;
3
4 use Shaarli\Api\Exceptions\ApiAuthorizationException;
5 use Shaarli\Http\Base64Url;
6
7 /**
8 * REST API utilities
9 */
10 class ApiUtils
11 {
12 /**
13 * Validates a JWT token authenticity.
14 *
15 * @param string $token JWT token extracted from the headers.
16 * @param string $secret API secret set in the settings.
17 *
18 * @return bool true on success
19 *
20 * @throws ApiAuthorizationException the token is not valid.
21 */
22 public static function validateJwtToken($token, $secret)
23 {
24 $parts = explode('.', $token);
25 if (count($parts) != 3 || strlen($parts[0]) == 0 || strlen($parts[1]) == 0) {
26 throw new ApiAuthorizationException('Malformed JWT token');
27 }
28
29 $genSign = Base64Url::encode(hash_hmac('sha512', $parts[0] .'.'. $parts[1], $secret, true));
30 if ($parts[2] != $genSign) {
31 throw new ApiAuthorizationException('Invalid JWT signature');
32 }
33
34 $header = json_decode(Base64Url::decode($parts[0]));
35 if ($header === null) {
36 throw new ApiAuthorizationException('Invalid JWT header');
37 }
38
39 $payload = json_decode(Base64Url::decode($parts[1]));
40 if ($payload === null) {
41 throw new ApiAuthorizationException('Invalid JWT payload');
42 }
43
44 if (empty($payload->iat)
45 || $payload->iat > time()
46 || time() - $payload->iat > ApiMiddleware::$TOKEN_DURATION
47 ) {
48 throw new ApiAuthorizationException('Invalid JWT issued time');
49 }
50
51 return true;
52 }
53
54 /**
55 * Format a Link for the REST API.
56 *
57 * @param array $link Link data read from the datastore.
58 * @param string $indexUrl Shaarli's index URL (used for relative URL).
59 *
60 * @return array Link data formatted for the REST API.
61 */
62 public static function formatLink($link, $indexUrl)
63 {
64 $out['id'] = $link['id'];
65 // Not an internal link
66 if (! is_note($link['url'])) {
67 $out['url'] = $link['url'];
68 } else {
69 $out['url'] = $indexUrl . $link['url'];
70 }
71 $out['shorturl'] = $link['shorturl'];
72 $out['title'] = $link['title'];
73 $out['description'] = $link['description'];
74 $out['tags'] = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY);
75 $out['private'] = $link['private'] == true;
76 $out['created'] = $link['created']->format(\DateTime::ATOM);
77 if (! empty($link['updated'])) {
78 $out['updated'] = $link['updated']->format(\DateTime::ATOM);
79 } else {
80 $out['updated'] = '';
81 }
82 return $out;
83 }
84
85 /**
86 * Convert a link given through a request, to a valid link for LinkDB.
87 *
88 * If no URL is provided, it will generate a local note URL.
89 * If no title is provided, it will use the URL as title.
90 *
91 * @param array $input Request Link.
92 * @param bool $defaultPrivate Request Link.
93 *
94 * @return array Formatted link.
95 */
96 public static function buildLinkFromRequest($input, $defaultPrivate)
97 {
98 $input['url'] = ! empty($input['url']) ? cleanup_url($input['url']) : '';
99 if (isset($input['private'])) {
100 $private = filter_var($input['private'], FILTER_VALIDATE_BOOLEAN);
101 } else {
102 $private = $defaultPrivate;
103 }
104
105 $link = [
106 'title' => ! empty($input['title']) ? $input['title'] : $input['url'],
107 'url' => $input['url'],
108 'description' => ! empty($input['description']) ? $input['description'] : '',
109 'tags' => ! empty($input['tags']) ? implode(' ', $input['tags']) : '',
110 'private' => $private,
111 'created' => new \DateTime(),
112 ];
113 return $link;
114 }
115
116 /**
117 * Update link fields using an updated link object.
118 *
119 * @param array $oldLink data
120 * @param array $newLink data
121 *
122 * @return array $oldLink updated with $newLink values
123 */
124 public static function updateLink($oldLink, $newLink)
125 {
126 foreach (['title', 'url', 'description', 'tags', 'private'] as $field) {
127 $oldLink[$field] = $newLink[$field];
128 }
129 $oldLink['updated'] = new \DateTime();
130
131 if (empty($oldLink['url'])) {
132 $oldLink['url'] = '?' . $oldLink['shorturl'];
133 }
134
135 if (empty($oldLink['title'])) {
136 $oldLink['title'] = $oldLink['url'];
137 }
138
139 return $oldLink;
140 }
141
142 /**
143 * Format a Tag for the REST API.
144 *
145 * @param string $tag Tag name
146 * @param int $occurrences Number of links using this tag
147 *
148 * @return array Link data formatted for the REST API.
149 */
150 public static function formatTag($tag, $occurences)
151 {
152 return [
153 'name' => $tag,
154 'occurrences' => $occurences,
155 ];
156 }
157 }