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

Остання зміна 2024/05/16 11:33 автором Ashterix

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

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

Блок security

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

Параметр protected_methods

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

За замовченням ввімкнут захист лише для методу POST. Ви можете:

  • вказати пустий масив [] щоб зробити API повністю відкритим
config/packages/ufo_json_rpc.yaml
1
2
3
ufo_json_rpc:
   security:
       protected_methods: []
  • вказати додатково захист для методу GET, що зробить запит документації недоступним без токену в заголовках запиту
config/packages/ufo_json_rpc.yaml
1
2
3
ufo_json_rpc:
   security:
       protected_methods: ['GET', 'POST']

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

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

Параметр token_key_in_header

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


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

Параметр clients_tokens

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

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

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

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

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

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

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

config/packages/ufo_json_rpc.yaml
1
2
3
4
5
6
7
ufo_json_rpc:
   security:
       protected_methods: ['GET', 'POST']
       token_key_in_header: '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
7
ufo_json_rpc:
   security:
       protected_methods: ['GET', 'POST']
       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 

Секція response

Містить налаштування, що відповідають за вміст відповіді.

Параметр key_for_methods

Цей параметр дозволяє вказати назву ключа у відповіді в якій буде віддаватися масив доступних сервісів.

Значення за замовченням methods. Може мати будь-яке значення типу рядок.

config/packages/ufo_json_rpc.yaml
1
2
3
ufo_json_rpc:
   docs:
       key_for_methods: methods
config/packages/ufo_json_rpc.yaml
1
2
3
ufo_json_rpc:
   docs:
       key_for_methods: services
config/packages/ufo_json_rpc.yaml
1
2
3
ufo_json_rpc:
   docs:
       key_for_methods: some_custom_key

Параметр async_dsn_info

Відповідає за відображення в документації інформації про асинхронний транспорт.

config/packages/ufo_json_rpc.yaml
1
2
ufo_json_rpc:
   async_dsn_info: true # or false

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

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
{
   "envelope": "JSON-RPC-2.0/UFO-RPC-6",
   "contentType": "application/json",
   "description": "",
   "transport": {
       "sync": {
           "scheme": "http",
           "host": "example.com",
           "path": "/api",
           "method": "POST"
        },
       "async": {
           "scheme": "amqp",
           "user": "{user}",
           "pass": "{pass}",
           "host": "async_rabbit",
           "port": 5672,
           "path": "/%2f/json-rpc"
        }
    },
   "methods": {
       ...
    }
}

Не переймайтеся щодо безпеки ваших авторизаційних даних. що містяться в 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}"
    }
}

Параметр validations

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

Наразі цей блок має два можливих налаштування:

  • json_schema: <bool>
  • symfony_asserts: <bool>

У всіх опцій в цьому параметрі значення за замовченням false, тобто ці блоки не будуть відображатися в документації.
Якщо ви потребуєте якийсь з цих блоків інформації при запиті документації, то встановіть значення в true.

config/packages/ufo_json_rpc.yaml
1
2
3
4
ufo_json_rpc:
   docs:
       json_schema: true
       symfony_asserts: true 

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

В цьому прикладі я видалив вміст обʼєктів symfony_assertions для спрощення прикладу.
Детальніше про валідацію методів дивись сторінку Валідація процедур

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
{
   "envelope": "JSON-RPC-2.0/UFO-RPC-6",
   "contentType": "application/json",
   "description": "",
   "transport": {
       "sync": {
           "scheme": "https",
           "host": "example.com",
           "path": "/api",
           "method": "POST"
        }
    },
   "methods": {
       "getUserNameByUuid": {
           "name": "getUserNameByUuid",
           "description": "Get username by id",
           "parameters": {
               "userId": {
                   "type": "string",
                   "name": "userId",
                   "description": "User id in uuid format",
                   "optional": false
                }
            },
           "returns": "string",
           "responseFormat": "string",
           "json_schema": {
               "$schema": "http://json-schema.org/draft-07/schema#",
               "type": "object",
               "properties": {
                   "userId": {
                       "type": "string"
                    }
                },
               "required": [
                   "userId"
                ]
            },
           "symfony_assertions": {
               "userId": [
                    {
                       "class": "Symfony\\Component\\Validator\\Constraints\\Uuid",
                       "context": {}
                    }
                ]
            }
        },
       "sendEmail": {
           "name": "sendEmail",
           "description": "Send mail",
           "parameters": {
               "email": {
                   "type": "string",
                   "name": "email",
                   "description": "",
                   "optional": false
                },
               "subject": {
                   "type": "string",
                   "name": "subject",
                   "description": "",
                   "optional": false
                },
               "text": {
                   "type": "string",
                   "name": "text",
                   "description": "",
                   "optional": false
                }
            },
           "returns": "boolean",
           "responseFormat": "boolean",
           "json_schema": {
               "$schema": "http://json-schema.org/draft-07/schema#",
               "type": "object",
               "properties": {
                   "email": {
                       "type": "string",
                       "format": "email"
                    },
                   "subject": {
                       "type": "string",
                       "minLength": 1,
                       "maxLength": 100
                    },
                   "text": {
                       "type": "string",
                       "minLength": 10
                    }
                },
               "required": [
                   "email",
                   "subject",
                   "text"
                ]
            },
           "symfony_assertions": {
               "email": [
                    {
                       "class": "Symfony\\Component\\Validator\\Constraints\\Email",
                       "context": {}
                    }
                ],
               "subject": [
                    {
                       "class": "Symfony\\Component\\Validator\\Constraints\\NotBlank",
                       "context": {}
                    },
                    {
                       "class": "Symfony\\Component\\Validator\\Constraints\\Length",
                       "context": {}
                    }
                ],
               "text": [
                    {
                       "class": "Symfony\\Component\\Validator\\Constraints\\NotBlank",
                       "context": {}
                    },
                    {
                       "class": "Symfony\\Component\\Validator\\Constraints\\Length",
                       "context": {}
                    }
                ]
            }
        }
    }
}