Зміни в документі Версія 6: Налаштування
Остання зміна 2024/07/11 10:07 автором Ashterix
Підсумок
-
Властивості сторінки (3 змінено, 0 додано, 0 видалено)
Подробиці
- Властивості сторінки
-
- Назва
-
... ... @@ -1,1 +1,1 @@ 1 -Налаштування бандлу1 +Версія 6: Налаштування - Батько
-
... ... @@ -1,1 +1,1 @@ 1 - Main.WebHome1 +WebHome - Вміст
-
... ... @@ -1,4 +1,4 @@ 1 -{{box cssClass="floatinginfobox" width="400px" title="**Зміст**"}}1 +{{box cssClass="floatinginfobox" title="**Зміст**"}} 2 2 {{toc/}} 3 3 {{/box}} 4 4 ... ... @@ -8,11 +8,11 @@ 8 8 (% class="wikigeneratedid" %) 9 9 Є можливість налаштувати параметри захисту API та деякі параметри формату даних, що віддається при запиті документації. 10 10 11 -= {{code language="none"}}security{{/code}} = 11 += Блок {{code language="none"}}security{{/code}} = 12 12 13 13 Наразі єдиним механізмом захисту доступу до вашого API є встановлення перевірки ключа доступу (api_token). 14 14 15 -== {{code language="none"}}protected_methods{{/code}} == 15 +== Параметр {{code language="none"}}protected_methods{{/code}} == 16 16 17 17 (% class="wikigeneratedid" %) 18 18 Цей параметр приймає масив назв http методів, які мають бути захищені. ... ... @@ -22,8 +22,7 @@ 22 22 23 23 * вказати пустий масив {{code language="none"}}[]{{/code}} щоб зробити API повністю відкритим 24 24 25 -{{code language="yaml"}} 26 -# config/packages/ufo_json_rpc.yaml 25 +{{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}} 27 27 ufo_json_rpc: 28 28 security: 29 29 protected_methods: [] ... ... @@ -31,8 +31,7 @@ 31 31 32 32 * вказати додатково захист для методу GET, що зробить запит документації недоступним без токену в заголовках запиту 33 33 34 -{{code language="yaml"}} 35 -# config/packages/ufo_json_rpc.yaml 33 +{{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}} 36 36 ufo_json_rpc: 37 37 security: 38 38 protected_methods: ['GET', 'POST'] ... ... @@ -42,30 +42,391 @@ 42 42 43 43 Перш за все, треба визначитися з назвою токену. 44 44 45 -== {{code language="none"}}token_key_in_header{{/code}} == 43 +== Параметр {{code language="none"}}token_key_in_header{{/code}} == 46 46 47 -Компонент {{code language="none"}}RpcSecurity{{/code}} буде шукати в заголовках запиту специфічний ключ, який ви можете встановити в налаштуваннях пакету, значення за замовченням {{code language="none"}}token_key_in_header: 'Ufo-RPC-Token'{{/code}}, ви можете встановити будь-яке інше значення яке відповідає умовам формуваннязаголовків протоколу HTTP45 +Компонент {{code language="none"}}RpcSecurity{{/code}} буде шукати в заголовках запиту специфічний ключ, який ви можете встановити в налаштуваннях пакету, значення за замовченням {{code language="none"}}token_key_in_header: 'Ufo-RPC-Token'{{/code}}, ви можете встановити будь-яке інше значення яке відповідає наступним вимогам. 48 48 47 +{{spoiler title=" Вимоги до формування заголовків протоколу HTTP"}} 48 +Вимоги до назв заголовків HTTP не є строго регульованими щодо капіталізації, оскільки HTTP заголовки нечутливі до регістру. Однак, існують деякі загальні практики і стандарти, які зазвичай дотримуються для кращої читабельності та узгодженості: 49 49 50 -Розглянемо варіант що у вас 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}} 51 51 52 52 53 53 54 54 55 55 60 +== Параметр {{code language="none"}}clients_tokens{{/code}} == 56 56 57 -{{code language="yaml"}} 58 -# config/packages/ufo_json_rpc.yaml 62 +Тепер слід вказати масив клієнтськіх токенів, які будуть мати доступ до API. 63 + 64 +Тут є певна варіативність. 65 + 66 +=== Токени в параметрах === 67 + 68 +(% class="box errormessage" %) 69 +((( 70 +**НЕ РЕКОМЕНДОВАНО!!!** 71 +\\Цей підхід допускається лише для локального тестування API 72 +))) 73 + 74 +Є можливість прописати токени хардкодом прямо в файлі налаштувань. 75 + 76 +Це погано з позиції безпеки, якщо код зберігається в публічному репозиторію, то до цього токену буде мати доступ кожен. 77 + 78 +{{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}} 59 59 ufo_json_rpc: 60 60 security: 61 - protected_methods: ['GET', 'POST'] # protection of GET and POST requests62 - token_key_in_header: 'Ufo-RPC-Token' # Name of the key in the header81 + protected_methods: ['GET', 'POST'] 82 + token_key_in_header: 'Ufo-RPC-Token' 63 63 clients_tokens: 64 - - 'ClientTokenExample' 65 - - '%env(resolve:UFO_API_TOKEN)%e' # token example from .env.local84 + - 'ClientTokenExample' # hardcoded token example. Importantly!!! Replace or delete it! 85 + 66 66 {{/code}} 67 67 88 +=== Токени в змінних оточення === 68 68 69 - ====90 +Це найбільш доцільний механізм у разі, якщо ви розробляєте сервіс для розподіленого бекенду, що написаний на SOA (Сервіс-Орієнтована Архітектура). Зазвичай, в такому випадку, вам треба відкрити доступ до апі одному або обмеженій кількості клієнтських додатків і оновлення ключів не буде відбуватися занадто часто. 70 70 71 - 92 +В такому випадку можна прописати токени в змінних оточення (файл {{code language="none"}}.env.local{{/code}} під час локальної розробки). Цей механізм достатньо безпечний з боку збереження доступів. 93 + 94 +((( 95 +{{code language="ini" layout="LINENUMBERS" title=".env.local"}} 96 +TOKEN_FOR_APP_1=9363074966579432364f8b73b3318f71 97 +TOKEN_FOR_APP_2=456fg87g8h98jmnb8675r4445n8up365 98 +{{/code}} 99 + 100 +{{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}} 101 +ufo_json_rpc: 102 + security: 103 + protected_methods: ['GET', 'POST'] 104 + token_key_in_header: 'Ufo-RPC-Token' 105 + clients_tokens: 106 + - '%env(resolve:TOKEN_FOR_APP_1)%' # token example from .env.local 107 + - '%env(resolve:TOKEN_FOR_APP_2)%' # token example from .env.local 108 +{{/code}} 109 +))) 110 + 111 +=== Токени для користувача === 112 + 113 +Припускаю, що у вас може виникнути потреба зробити персональні ключі для користувачів вашого додатку, можливо ви захочете впровадити ліміти або інші обмеження. 114 +В такому випадку вам не потрібно вказувати перелік токенів в конфігах, ви можете зберігати їх в базі даних або іншому місці згідно вашій бізнес-логіки. Єдина вимога, у вас має бути сервіс, який вміє перевіряти чи існує наданий токен. 115 + 116 +Для того, щоб JsonRpcServer міг використовувати вашу логіку, доведеться реалізувати власний клас, що реалізує інтерфейс {{code language="none"}}Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator{{/code}} 117 + 118 +{{code language="php" layout="LINENUMBERS" title="==== Приклад власного валідатора токенів ===="}} 119 +<?php 120 + 121 +namespace App\Services\RpcSecurity; 122 + 123 +use App\Services\UserService; 124 +use Symfony\Component\Security\Core\Exception\UserNotFoundException; 125 +use Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator; 126 +use Ufo\RpcError\RpcInvalidTokenException; 127 + 128 +class UserTokenValidator implements ITokenValidator 129 +{ 130 + 131 + public function __construct(protected UserService $userService) {} 132 + 133 + public function isValid(string $token): true 134 + { 135 + try { 136 + $this->userService->getUserByToken($token); 137 + return true; 138 + } catch (UserNotFoundException $e) { 139 + throw new RpcInvalidTokenException(previous: $e); 140 + } 141 + } 142 +} 143 +{{/code}} 144 + 145 +(% class="box warningmessage" %) 146 +((( 147 +**ВАЖЛИВО!!!** 148 +Метод {{code language="none"}}isValid{{/code}} має повертати {{code language="none"}}true{{/code}} якщо токен існує і валідний, або викидати {{code language="none"}}Ufo\RpcError\RpcInvalidTokenException{{/code}} в іншому разі. 149 +))) 150 + 151 +Після цього вам потрібно в файлі {{code language="none"}}config/services.yaml{{/code}} прописати що класи, що мають залежність від інтерфейса {{code language="none"}}ITokenValidator{{/code}} мають приймати ваш новий клас. 152 + 153 +{{code language="yaml" layout="LINENUMBERS" title="config/services.yaml"}} 154 +parameters: 155 + # some parameters list 156 + # ... 157 + 158 +services: 159 + # some services list 160 + # ... 161 + 162 + Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator: 163 + class: App\Services\RpcSecurity\UserTokenValidator 164 +{{/code}} 165 + 166 += Блок {{code language="none"}}async{{/code}} = 167 + 168 +Цей блок для налаштування [[асинхронного транспорту>>doc:docs.JsonRpcBundle.functionality.async.WebHome]]. 169 + 170 +Додайте параметр {{code language="none"}}rpc_async{{/code}} який містить рядок у форматі DSN. Цей рядок є конфігурацією [[Symfony Messenger>>https://symfony.com/doc/current/messenger.html]], він вказує на асинхронний транспорт по якому RPC Server буде очікувати вхідні запити якщо у вас запущений консюмер ({{code language="none"}}php bin/console messenger:consume rpc_async{{/code}}). Для більш детального розуміння цього процесу читайте документацію [[Symfony Messenge>>https://symfony.com/doc/current/messenger.html]]. 171 + 172 +{{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}} 173 +ufo_json_rpc: 174 + async: 175 + rpc_async: '%env(resolve:RPC_TRANSPORT_DSN)%' 176 + 177 +{{/code}} 178 + 179 +(% class="box warningmessage" %) 180 +((( 181 +Це налаштування має на увазі, що у вас в змінних оточення встановлена змінна RPC_TRANSPORT_DSN що містить DSN рядок. 182 +))) 183 + 184 += Блок {{code language="none"}}docs{{/code}} = 185 + 186 +Це блок який налаштовує генерацію документації коли ви робите GET запит на RPC Server 187 + 188 +== Секція {{code language="none"}}response{{/code}} == 189 + 190 +Містить налаштування, що відповідають за вміст відповіді. 191 + 192 +=== Параметр {{code language="none"}}key_for_methods{{/code}} === 193 + 194 +Цей параметр дозволяє вказати назву ключа у відповіді в якій буде віддаватися масив доступних сервісів. 195 + 196 +Значення за замовченням {{code language="none"}}methods{{/code}}. Може мати будь-яке значення типу рядок. 197 + 198 +{{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}} 199 +ufo_json_rpc: 200 + docs: 201 + key_for_methods: methods 202 +{{/code}} 203 + 204 +{{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}} 205 +ufo_json_rpc: 206 + docs: 207 + key_for_methods: services 208 +{{/code}} 209 + 210 +{{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}} 211 +ufo_json_rpc: 212 + docs: 213 + key_for_methods: some_custom_key 214 +{{/code}} 215 + 216 +=== Параметр {{code language="none"}}async_dsn_info{{/code}} === 217 + 218 +Відповідає за відображення в документації інформації про асинхронний транспорт. 219 + 220 +{{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}} 221 +ufo_json_rpc: 222 + async_dsn_info: true # or false 223 +{{/code}} 224 + 225 +==== **Приклад документації ** ==== 226 + 227 +{{code language="json" layout="LINENUMBERS" title="GET: /api"}} 228 +{ 229 + "envelope": "JSON-RPC-2.0/UFO-RPC-6", 230 + "contentType": "application/json", 231 + "description": "", 232 + "transport": { 233 + "sync": { 234 + "scheme": "http", 235 + "host": "example.com", 236 + "path": "/api", 237 + "method": "POST" 238 + }, 239 + "async": { 240 + "scheme": "amqp", 241 + "user": "{user}", 242 + "pass": "{pass}", 243 + "host": "async_rabbit", 244 + "port": 5672, 245 + "path": "/%2f/json-rpc" 246 + } 247 + }, 248 + "methods": { 249 + ... 250 + } 251 +} 252 +{{/code}} 253 + 254 +{{info}} 255 +Не переймайтеся щодо безпеки ваших авторизаційних даних. що містяться в DSN. 256 + 257 +Документатор побудований таким чином, що перед виводом інформації про DSN він видаляє дані про користувача і його пароль, а також інші секретні дані, як то токени, секретні ключі, тощо. 258 + 259 +Шаблон, по якому відбувається захист {{code language="none"}}/([\w\d_]*(?:secret|access|token|key)[_\w]*)=((?:\w|\d)+(?=&?))/{{/code}}. 260 + 261 +Приклад: 262 + 263 +{{code language="json" layout="LINENUMBERS" title="RPC_TRANSPORT_DSN=https://sqs.eu-west-3.amazonaws.com/123456789012/messages?access_key=AKIAIOSFODNN7EXAMPLE&secret_key=j17M97ffSVoKI0briFoo9a"}} 264 +{ 265 + "async": { 266 + "scheme": "https", 267 + "host": "sqs.eu-west-3.amazonaws.com", 268 + "path": "/123456789012/messages", 269 + "query": "access_key={access_key}&secret_key={secret_key}" 270 + } 271 +} 272 +{{/code}} 273 +{{/info}} 274 + 275 +=== Параметр {{code language="none"}}validations{{/code}} === 276 + 277 +Відповідає за відображення в документації методів додаткових блоків, що вказують на вимоги до валідації даних. 278 + 279 +Наразі цей блок має два можливих налаштування: 280 + 281 +* {{code language="none"}}json_schema: <bool>{{/code}} 282 +* {{code language="none"}}symfony_asserts: <bool>{{/code}} 283 + 284 +У всіх опцій в цьому параметрі значення за замовченням {{code language="none"}}false{{/code}}, тобто ці блоки не будуть відображатися в документації. 285 +Якщо ви потребуєте якийсь з цих блоків інформації при запиті документації, то встановіть значення в {{code language="none"}}true{{/code}}. 286 + 287 +{{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}} 288 +ufo_json_rpc: 289 + docs: 290 + json_schema: true 291 + symfony_asserts: true 292 +{{/code}} 293 + 294 +==== **Приклад документації ** ==== 295 + 296 +(% class="box infomessage" %) 297 +((( 298 +В цьому прикладі я видалив вміст обʼєктів symfony_assertions для спрощення прикладу. 299 +Детальніше про валідацію методів дивись сторінку **[[Валідація процедур>>doc:docs.JsonRpcBundle.add_rpc_service.assertions.WebHome]]** 300 +))) 301 + 302 +{{code language="json" layout="LINENUMBERS" title="GET: /api"}} 303 +{ 304 + "envelope": "JSON-RPC-2.0/UFO-RPC-6", 305 + "contentType": "application/json", 306 + "description": "", 307 + "transport": { 308 + "sync": { 309 + "scheme": "https", 310 + "host": "example.com", 311 + "path": "/api", 312 + "method": "POST" 313 + } 314 + }, 315 + "methods": { 316 + "getUserNameByUuid": { 317 + "name": "getUserNameByUuid", 318 + "description": "Get username by id", 319 + "parameters": { 320 + "userId": { 321 + "type": "string", 322 + "name": "userId", 323 + "description": "User id in uuid format", 324 + "optional": false 325 + } 326 + }, 327 + "returns": "string", 328 + "responseFormat": "string", 329 + "json_schema": { 330 + "$schema": "http://json-schema.org/draft-07/schema#", 331 + "type": "object", 332 + "properties": { 333 + "userId": { 334 + "type": "string" 335 + } 336 + }, 337 + "required": [ 338 + "userId" 339 + ] 340 + }, 341 + "symfony_assertions": { 342 + "userId": [ 343 + { 344 + "class": "Symfony\\Component\\Validator\\Constraints\\Uuid", 345 + "context": {} 346 + } 347 + ] 348 + } 349 + }, 350 + "sendEmail": { 351 + "name": "sendEmail", 352 + "description": "Send mail", 353 + "parameters": { 354 + "email": { 355 + "type": "string", 356 + "name": "email", 357 + "description": "", 358 + "optional": false 359 + }, 360 + "subject": { 361 + "type": "string", 362 + "name": "subject", 363 + "description": "", 364 + "optional": false 365 + }, 366 + "text": { 367 + "type": "string", 368 + "name": "text", 369 + "description": "", 370 + "optional": false 371 + } 372 + }, 373 + "returns": "boolean", 374 + "responseFormat": "boolean", 375 + "json_schema": { 376 + "$schema": "http://json-schema.org/draft-07/schema#", 377 + "type": "object", 378 + "properties": { 379 + "email": { 380 + "type": "string", 381 + "format": "email" 382 + }, 383 + "subject": { 384 + "type": "string", 385 + "minLength": 1, 386 + "maxLength": 100 387 + }, 388 + "text": { 389 + "type": "string", 390 + "minLength": 10 391 + } 392 + }, 393 + "required": [ 394 + "email", 395 + "subject", 396 + "text" 397 + ] 398 + }, 399 + "symfony_assertions": { 400 + "email": [ 401 + { 402 + "class": "Symfony\\Component\\Validator\\Constraints\\Email", 403 + "context": {} 404 + } 405 + ], 406 + "subject": [ 407 + { 408 + "class": "Symfony\\Component\\Validator\\Constraints\\NotBlank", 409 + "context": {} 410 + }, 411 + { 412 + "class": "Symfony\\Component\\Validator\\Constraints\\Length", 413 + "context": {} 414 + } 415 + ], 416 + "text": [ 417 + { 418 + "class": "Symfony\\Component\\Validator\\Constraints\\NotBlank", 419 + "context": {} 420 + }, 421 + { 422 + "class": "Symfony\\Component\\Validator\\Constraints\\Length", 423 + "context": {} 424 + } 425 + ] 426 + } 427 + } 428 + } 429 +} 430 +{{/code}}