2. Налаштування бандла

Остання зміна 2024/07/11 11:49 автором Ashterix

Конфігурація зазнала суттєвих змін і не має зворотної сумісності з версією 6.

Всі налаштування бандла знаходяться в файлі config/packages/ufo_json_rpc.yaml.

Є можливість налаштувати параметри захисту API та деякі параметри формату даних, що віддається при запиті документації.

Блок security

Наразі єдиним механізмом захисту доступу до вашого API є встановлення перевірки ключа доступу (api_token).

Параметри protected_api та protected_doc (boolean)

Ці параметри вказує чи мають бути захищені API методи та документація, відповідно.

За замовченням апі методи захищені, а документація відкрита.

config/packages/ufo_json_rpc.yaml
1
2
3
4
ufo_json_rpc:
   security:
       protected_api: true     # Protection API requests
       protected_doc: false    # Protection API documentation

Якщо ви захищаєте ваш API через protected_api, вам необхідно налаштувати токени, по яким буде відкритий доступ.

Перш за все, треба визначитися з назвою токену.

Параметр token_name

Компонент RpcSecurity буде шукати в заголовках запиту специфічний ключ, який ви можете встановити в налаштуваннях пакету, значення за замовченням token_name: 'Ufo-RPC-Token', ви можете встановити будь-яке інше значення яке відповідає наступним вимогам.


Вимоги до формування заголовків протоколу HTTP

Параметр clients_tokens

Тепер слід вказати масив клієнтськіх токенів, які будуть мати доступ до API.

Тут є певна варіативність.

Токени в параметрах

НЕ РЕКОМЕНДОВАНО!!!

Цей підхід допускається лише для локального тестування API

Є можливість прописати токени хардкодом прямо в файлі налаштувань.

Це погано з позиції безпеки, якщо код зберігається в публічному репозиторію, то до цього токену буде мати доступ кожен.

config/packages/ufo_json_rpc.yaml
1
2
3
4
5
6
ufo_json_rpc:
   security:
       token_name: 'Ufo-RPC-Token'
       clients_tokens:
            - 'ClientTokenExample'  # hardcoded token example. Importantly!!! Replace or delete it!
            

Токени в змінних оточення

Це найбільш доцільний механізм у разі, якщо ви розробляєте сервіс для розподіленого бекенду, що написаний на SOA (Сервіс-Орієнтована Архітектура). Зазвичай, в такому випадку, вам треба відкрити доступ до апі одному або обмеженій кількості клієнтських додатків і оновлення ключів не буде відбуватися занадто часто.

В такому випадку можна прописати токени в змінних оточення (файл .env.local під час локальної розробки). Цей механізм достатньо безпечний з боку збереження доступів.

.env.local
1
2
TOKEN_FOR_APP_1=9363074966579432364f8b73b3318f71
TOKEN_FOR_APP_2=456fg87g8h98jmnb8675r4445n8up365
config/packages/ufo_json_rpc.yaml
1
2
3
4
5
6
ufo_json_rpc:
   security:
       token_key_in_header: 'Ufo-RPC-Token'
       clients_tokens:
            - '%env(resolve:TOKEN_FOR_APP_1)%'   # token example from .env.local
            - '%env(resolve:TOKEN_FOR_APP_2)%'   # token example from .env.local

Токени для користувача

Припускаю, що у вас може виникнути потреба зробити персональні ключі для користувачів вашого додатку, можливо ви захочете впровадити ліміти або інші обмеження.
В такому випадку вам не потрібно вказувати перелік токенів в конфігах, ви можете зберігати їх в базі даних або іншому місці згідно вашій бізнес-логіки. Єдина вимога, у вас має бути сервіс, який вміє перевіряти чи існує наданий токен. 

Для того, щоб JsonRpcServer міг використовувати вашу логіку, доведеться реалізувати власний клас, що реалізує інтерфейс Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator

Приклад власного валідатора токенів

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\Services\RpcSecurity;

use App\Services\UserService;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator;
use Ufo\RpcError\RpcInvalidTokenException;

class UserTokenValidator implements ITokenValidator
{

   public function __construct(protected UserService $userService) {}

   public function isValid(string $token): true
   {
       try {
           $this->userService->getUserByToken($token);
           return true;
        } catch (UserNotFoundException $e) {
           throw new RpcInvalidTokenException(previous: $e);
        }
    }
}

ВАЖЛИВО!!!
Метод isValid має повертати true якщо токен існує і валідний, або викидати Ufo\RpcError\RpcInvalidTokenException в іншому разі.

Після цього вам потрібно в файлі config/services.yaml прописати що класи, що мають залежність від інтерфейса ITokenValidator мають приймати ваш новий клас.

config/services.yaml
1
2
3
4
5
6
7
8
9
10
parameters:
  # some parameters list
  # ...

services:
  # some services list
  # ...

   Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator:
       class: App\Services\RpcSecurity\UserTokenValidator

Блок async

Цей блок для налаштування асинхронного транспорту.

Додайте параметр rpc_async який містить рядок у форматі DSN. Цей рядок є конфігурацією Symfony Messenger, він вказує на асинхронний транспорт по якому RPC Server буде очікувати вхідні запити якщо у вас запущений консюмер (php bin/console messenger:consume rpc_async). Для більш детального розуміння цього процесу читайте документацію Symfony Messenge.

config/packages/ufo_json_rpc.yaml
1
2
3
4
ufo_json_rpc:
   async:
       rpc_async: '%env(resolve:RPC_TRANSPORT_DSN)%'

Це налаштування має на увазі, що у вас в змінних оточення встановлена змінна RPC_TRANSPORT_DSN що містить DSN рядок.

Блок docs

Цей блок налаштовує деякі параметри генерації документації коли ви робите GET запит на RPC Server.

Починаючи з версії 7, JsonRpcBundle генерує API документацію, що відповідає специфікації OpenRpc

  • project_name: Назва проєкту, що буде відображена в документації
  • project_description: Опис проєкту
  • project_version: Поточна версія вашого API
  • async_dsn_info: Відповідає за відображення в документації інформації про асинхронний транспорт
  • validations.symfony_asserts: <bool> Відповідає за відображення рядку очікувань валідації для параметра (якщо ви використовуєте валідацію)
config/packages/ufo_json_rpc.yaml
1
2
3
4
5
6
7
8
9
10
ufo_json_rpc:
   docs:
       project_name: 'My Project'
       project_description: 'My project description'
       project_version: '1.0'
       # Optional response details
       async_dsn_info:  false          # Provide information about API that work asynchronously  
       validations:
           symfony_asserts: false      # Indicates if an array of Symfony validation constraints is used

Не переймайтеся щодо безпеки ваших авторизаційних даних, що містяться в DSN.

Документатор побудований таким чином, що перед виводом інформації про DSN він видаляє дані про користувача і його пароль, а також інші секретні дані, як то токени, секретні ключі, тощо.

Шаблон, по якому відбувається захист /([\w\d_]*(?:secret|access|token|key)[_\w]*)=((?:\w|\d)+(?=&?))/.

Приклад: 

RPC_TRANSPORT_DSN=https://sqs.eu-west-3.amazonaws.com/123456789012/messages?access_key=AKIAIOSFODNN7EXAMPLE&secret_key=j17M97ffSVoKI0briFoo9a
1
2
3
4
5
6
7
8
{
   "async": {
       "scheme": "https",
       "host": "sqs.eu-west-3.amazonaws.com",
       "path": "/123456789012/messages",
       "query": "access_key={access_key}&secret_key={secret_key}"
    }
}

Приклад документації

GET: /api
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
{
  "openrpc":"1.2.6",
  "info":{
     "title":"My Project",
     "description":"My project description",
     "contact":{
        "name":"ufo-tech/json-rpc-bundle",
        "url":"https://docs.ufo-tech.space/bin/view/docs/JsonRpcBundle/?language=en"
      },
     "license":{
        "name":"MIT"
      },
     "version":"1.0"
   },
  "servers":[
      {
        "url":"https://mysite.com/api",
        "description":"Json-RPC api server from UFO Tec\n\nUFO Tech, or Universal Flexible Open Technologies, is an initiative aimed at providing PHP developers with tools to create complex yet user-friendly solutions for modern web applications and service-oriented architectures.",
        "name":"UFO Json-RPC Server v.7.0.0",
        "x-method":"POST",
        "x-ufo":{
           "envelop":"JSON-RPC-2.0/UFO-RPC-7.0.0",
           "transport":{
              "sync":{
                 "scheme":"https",
                 "host":"mysite.com",
                 "path":"/api",
                 "method":"POST"
               },
              "async":{
                 "scheme":"amqp",
                 "user":"{user}",
                 "pass":"{pass}",
                 "host":"mysite.com",
                 "port":5672,
                 "path":"/%2f/json-rpc"
               }
            },
           "documentation":{
              "json-rpc":"https://www.jsonrpc.org/specification",
              "ufo-tech/json-rpc-bundle":"https://docs.ufo-tech.space/bin/view/docs/JsonRpcBundle/?language=en"
            }
         }
      }
   ],
  "methods":[
      {
        "name":"getUserNameByUuid",
        "tags":[
            {
              "name":"App\\Api\\UserApiService"
            }
         ],
        "summary":"Get username by id",
        "params":[
            {
              "name":"userId",
              "description":"User Id format uuid",
              "required":true,
              "schema":{
                 "type":"string"
               },
              "x-ufo-assertions": "new Assert\\Uuid()"
            }
         ],
        "result":{
           "name":"string",
           "description":"User Name",
           "schema":{
              "type":"string"
            }
         }
      }
   ]
}