Версія 17.1 додана 2024/05/09 19:17 автором Ashterix

Показати останніх авторів
1 {{box cssClass="floatinginfobox" width="400px" title="**Зміст**"}}
2 {{toc/}}
3 {{/box}}
4
5 (% class="wikigeneratedid" %)
6 Всі налаштування бандла знаходяться в файлі {{code language="none"}}config/packages/ufo_json_rpc.yaml{{/code}}.
7
8 (% class="wikigeneratedid" %)
9 Є можливість налаштувати параметри захисту API та деякі параметри формату даних, що віддається при запиті документації.
10
11 = Блок {{code language="none"}}security{{/code}} =
12
13 Наразі єдиним механізмом захисту доступу до вашого API є встановлення перевірки ключа доступу (api_token).
14
15 == Параметр {{code language="none"}}protected_methods{{/code}} ==
16
17 (% class="wikigeneratedid" %)
18 Цей параметр приймає масив назв http методів, які мають бути захищені.
19
20 (% class="wikigeneratedid" %)
21 За замовченням ввімкнут захист лише для методу POST. Ви можете:
22
23 * вказати пустий масив {{code language="none"}}[]{{/code}} щоб зробити API повністю відкритим
24
25 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
26 ufo_json_rpc:
27 security:
28 protected_methods: []
29 {{/code}}
30
31 * вказати додатково захист для методу GET, що зробить запит документації недоступним без токену в заголовках запиту
32
33 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
34 ufo_json_rpc:
35 security:
36 protected_methods: ['GET', 'POST']
37 {{/code}}
38
39 (% id="cke_bm_164641S" style="display:none" %) (%%)Якщо ви захищаєте ваш API через {{code language="none"}}protected_methods{{/code}}, вам необхідно налаштувати токени, по яким буде відкритий доступ.
40
41 Перш за все, треба визначитися з назвою токену.
42
43 == Параметр {{code language="none"}}token_key_in_header{{/code}} ==
44
45 Компонент {{code language="none"}}RpcSecurity{{/code}} буде шукати в заголовках запиту специфічний ключ, який ви можете встановити в налаштуваннях пакету, значення за замовченням {{code language="none"}}token_key_in_header: 'Ufo-RPC-Token'{{/code}}, ви можете встановити будь-яке інше значення яке відповідає наступним вимогам.
46
47 {{spoiler title=" Вимоги до формування заголовків протоколу HTTP"}}
48 Вимоги до назв заголовків HTTP не є строго регульованими щодо капіталізації, оскільки HTTP заголовки нечутливі до регістру. Однак, існують деякі загальні практики і стандарти, які зазвичай дотримуються для кращої читабельності та узгодженості:
49
50 - Капіталізація: Зазвичай назви HTTP заголовків пишуться з використанням CamelCase, де кожне слово починається з великої літери, наприклад, Content-Type, User-Agent, Accept-Encoding. Це не впливає на технічну обробку заголовків, але робить їх легше читати.
51 - Унікальність: Кожен заголовок повинен мати унікальну назву у контексті одного HTTP запиту або відповіді. Не можна використовувати однакові назви для різних заголовків у тому самому запиті чи відповіді.
52 - Спеціальні заголовки: Існують заголовки, які використовуються специфічно для контролю поведінки кешування (Cache-Control), безпеки (Strict-Transport-Security), аутентифікації (Authorization), тощо.
53 - Норми RFC: Вимоги до заголовків регулюються документами RFC, які визначають стандарти для протоколів Інтернету. Наприклад, загальні заголовки і їхнє використання описані в RFC 7231.
54 {{/spoiler}}
55
56 == Параметр {{code language="none"}}clients_tokens{{/code}} ==
57
58 Тепер слід вказати масив клієнтськіх токенів, які будуть мати доступ до API.
59
60 Тут є певна варіативність.
61
62 === Токени в параметрах ===
63
64 (% class="box errormessage" %)
65 (((
66 **НЕ РЕКОМЕНДОВАНО!!!**
67 \\Цей підхід допускається лише для локального тестування API
68 )))
69
70 Є можливість прописати токени хардкодом прямо в файлі налаштувань.
71
72 Це погано з позиції безпеки, якщо код зберігається в публічному репозиторію, то до цього токену буде мати доступ кожен.
73
74 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
75 ufo_json_rpc:
76 security:
77 protected_methods: ['GET', 'POST']
78 token_key_in_header: 'Ufo-RPC-Token'
79 clients_tokens:
80 - 'ClientTokenExample' # hardcoded token example. Importantly!!! Replace or delete it!
81
82 {{/code}}
83
84 === Токени в змінних оточення ===
85
86 Це найбільш доцільний механізм у разі, якщо ви розробляєте сервіс для розподіленого бекенду, що написаний на SOA (Сервіс-Орієнтована Архітектура). Зазвичай, в такому випадку, вам треба відкрити доступ до апі одному або обмеженій кількості клієнтських додатків і оновлення ключів не буде відбуватися занадто часто.
87
88 В такому випадку можна прописати токени в змінних оточення (файл {{code language="none"}}.env.local{{/code}} під час локальної розробки). Цей механізм достатньо безпечний з боку збереження доступів.
89
90 (((
91 {{code language="ini" layout="LINENUMBERS" title=".env.local"}}
92 TOKEN_FOR_APP_1=9363074966579432364f8b73b3318f71
93 TOKEN_FOR_APP_2=456fg87g8h98jmnb8675r4445n8up365
94 {{/code}}
95
96 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
97 ufo_json_rpc:
98 security:
99 protected_methods: ['GET', 'POST']
100 token_key_in_header: 'Ufo-RPC-Token'
101 clients_tokens:
102 - '%env(resolve:TOKEN_FOR_APP_1)%' # token example from .env.local
103 - '%env(resolve:TOKEN_FOR_APP_2)%' # token example from .env.local
104 {{/code}}
105 )))
106
107 === Токени для користувача ===
108
109 Припускаю, що у вас може виникнути потреба зробити персональні ключі для користувачів вашого додатку, можливо ви захочете впровадити ліміти або інші обмеження.
110 В такому випадку вам не потрібно вказувати перелік токенів в конфігах, ви можете зберігати їх в базі даних або іншому місці згідно вашій бізнес-логіки. Єдина вимога, у вас має бути сервіс, який вміє перевіряти чи існує наданий токен.
111
112 Для того, щоб JsonRpcServer міг використовувати вашу логіку, доведеться реалізувати власний клас, що реалізує інтерфейс {{code language="none"}}Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator{{/code}}
113
114 {{code language="php" layout="LINENUMBERS" title="==== Приклад власного валідатора токенів ===="}}
115 <?php
116
117 namespace App\Services\RpcSecurity;
118
119 use App\Services\UserService;
120 use Symfony\Component\Security\Core\Exception\UserNotFoundException;
121 use Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator;
122 use Ufo\RpcError\RpcInvalidTokenException;
123
124 class UserTokenValidator implements ITokenValidator
125 {
126
127 public function __construct(protected UserService $userService) {}
128
129 public function isValid(string $token): bool
130 {
131 try {
132 $this->userService->getUserByToken($token);
133 return true;
134 } catch (UserNotFoundException $e) {
135 throw new RpcInvalidTokenException(previous: $e);
136 }
137 }
138 }
139 {{/code}}
140
141 (% class="box warningmessage" %)
142 (((
143 **ВАЖЛИВО!!!**
144 Метод {{code language="none"}}isValid{{/code}} має повертати {{code language="none"}}true{{/code}} якщо токен існує і валідний, або викидати {{code language="none"}}Ufo\RpcError\RpcInvalidTokenException{{/code}} в іншому разі.
145 )))
146
147 Після цього вам потрібно в файлі {{code language="none"}}config/services.yaml{{/code}} прописати що класи, що мають залежність від інтерфейса {{code language="none"}}ITokenValidator{{/code}} мають приймати ваш новий клас.
148
149 {{code language="yaml" layout="LINENUMBERS" title="config/services.yaml"}}
150 parameters:
151 # some parameters list
152 # ...
153
154 services:
155 # some services list
156 # ...
157
158 Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator:
159 class: App\Services\RpcSecurity\UserTokenValidator
160 {{/code}}
161
162 = Блок {{code language="none"}}docs{{/code}} =
163
164 Це блок який налаштовує генерацію документації коли ви робите GET запит на RPC Server
165
166 == Секція {{code language="none"}}response{{/code}} ==
167
168 Містить налаштування, що відповідають за вміст відповіді.
169
170 === Параметр {{code language="none"}}key_for_methods{{/code}} ===
171
172 Цей параметр дозволяє вказати назву ключа у відповіді в якій буде віддаватися масив доступних сервісів.
173
174 Значення за замовченням {{code language="none"}}methods{{/code}}. Може мати будь-яке значення типу рядок.
175
176 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
177 ufo_json_rpc:
178 docs:
179 key_for_methods: methods
180 {{/code}}
181
182 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
183 ufo_json_rpc:
184 docs:
185 key_for_methods: services
186 {{/code}}
187
188 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
189 ufo_json_rpc:
190 docs:
191 key_for_methods: some_custom_key
192 {{/code}}
193
194 === Параметр {{code language="none"}}validations{{/code}} ===
195
196 Відповідає за відображення в документації методів додаткових блоків, що вказують на вимоги до валідації даних.
197
198 Наразі цей блок має два можливих налаштування:
199
200 * {{code language="none"}}json_schema: <bool>{{/code}}
201 * {{code language="none"}}symfony_asserts: <bool>{{/code}}
202
203 У всіх опцій в цьому параметрі значення за замовченням {{code language="none"}}false{{/code}}, тобто ці блоки не будуть відображатися в документації.
204 Якщо ви потребуєте якийсь з цих блоків інформації при запиті документації, то встановіть значення в {{code language="none"}}true{{/code}}.
205
206 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
207 ufo_json_rpc:
208 docs:
209 json_schema: true
210 symfony_asserts: true
211 {{/code}}
212
213 ==== **Приклад документації ** ====
214
215 (% class="box infomessage" %)
216 (((
217 В цьому прикладі я видалив вміст обʼєктів symfony_assertions для спрощення прикладу.
218 Детальніше про валідацію методів дивись сторінку **[[Валідація процедур>>doc:.add_rpc_service.assertions.WebHome]]**
219 )))
220
221 {{code language="json" layout="LINENUMBERS" title="GET: /api"}}
222 {
223 "envelope": "JSON-RPC-2.0/UFO-RPC-6",
224 "transport": "POST",
225 "target": "/api",
226 "contentType": "application/json",
227 "description": "",
228 "methods": {
229 "getUserNameByUuid": {
230 "name": "getUserNameByUuid",
231 "description": "Get username by id",
232 "parameters": {
233 "userId": {
234 "type": "string",
235 "name": "userId",
236 "description": "User id in uuid format",
237 "optional": false
238 }
239 },
240 "returns": "string",
241 "responseFormat": "string",
242 "json_schema": {
243 "$schema": "http://json-schema.org/draft-07/schema#",
244 "type": "object",
245 "properties": {
246 "userId": {
247 "type": "string"
248 }
249 },
250 "required": [
251 "userId"
252 ]
253 },
254 "symfony_assertions": {
255 "userId": [
256 {
257 "class": "Symfony\\Component\\Validator\\Constraints\\Uuid",
258 "context": {}
259 }
260 ]
261 }
262 },
263 "sendEmail": {
264 "name": "sendEmail",
265 "description": "Send mail",
266 "parameters": {
267 "email": {
268 "type": "string",
269 "name": "email",
270 "description": "",
271 "optional": false
272 },
273 "subject": {
274 "type": "string",
275 "name": "subject",
276 "description": "",
277 "optional": false
278 },
279 "text": {
280 "type": "string",
281 "name": "text",
282 "description": "",
283 "optional": false
284 }
285 },
286 "returns": "boolean",
287 "responseFormat": "boolean",
288 "json_schema": {
289 "$schema": "http://json-schema.org/draft-07/schema#",
290 "type": "object",
291 "properties": {
292 "email": {
293 "type": "string",
294 "format": "email"
295 },
296 "subject": {
297 "type": "string",
298 "minLength": 1,
299 "maxLength": 100
300 },
301 "text": {
302 "type": "string",
303 "minLength": 10
304 }
305 },
306 "required": [
307 "email",
308 "subject",
309 "text"
310 ]
311 },
312 "symfony_assertions": {
313 "email": [
314 {
315 "class": "Symfony\\Component\\Validator\\Constraints\\Email",
316 "context": {}
317 }
318 ],
319 "subject": [
320 {
321 "class": "Symfony\\Component\\Validator\\Constraints\\NotBlank",
322 "context": {}
323 },
324 {
325 "class": "Symfony\\Component\\Validator\\Constraints\\Length",
326 "context": {}
327 }
328 ],
329 "text": [
330 {
331 "class": "Symfony\\Component\\Validator\\Constraints\\NotBlank",
332 "context": {}
333 },
334 {
335 "class": "Symfony\\Component\\Validator\\Constraints\\Length",
336 "context": {}
337 }
338 ]
339 }
340 }
341 }
342 }
343 {{/code}}