429 lines
13 KiB
Markdown
429 lines
13 KiB
Markdown
|
|
---
|
||
|
|
gitea: none
|
||
|
|
include_toc: true
|
||
|
|
---
|
||
|
|
|
||
|
|
# Construire une liste d'options
|
||
|
|
|
||
|
|
## La configuration du proxy type Mozilla Firefox
|
||
|
|
|
||
|
|
L'objectif de ce première exemple est de reproduire cette page de paramètres de Mozilla Firefox :
|
||
|
|
|
||
|
|

|
||
|
|
|
||
|
|
Les variables vont être créé dans plusieurs fichier dans un but didactique. Bien évidement toutes les options pourront être mise dans le même fichier.
|
||
|
|
|
||
|
|
|
||
|
|
### La famille proxy
|
||
|
|
|
||
|
|
Nous allons classer toutes ces variables dans une famille.
|
||
|
|
Cette famille s'appelera proxy. Créons le premier fichier dict/00-proxy.yml
|
||
|
|
|
||
|
|
```yml
|
||
|
|
---
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
description: Configure Proxy Access to the Internet
|
||
|
|
```
|
||
|
|
|
||
|
|
### Le type de proxy
|
||
|
|
|
||
|
|
Il est possible de définir plusieurs modes de configuration du proxy (de pas de proxy à la configuration d'un fichier de configuration automatique).
|
||
|
|
|
||
|
|
Nous allons donc créer une première variable dans cette famille dont la description est "Proxy mode". Créons le deuxième fichier dict/01-proxy\_mode.yml
|
||
|
|
|
||
|
|
```yml
|
||
|
|
---
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
proxy_mode:
|
||
|
|
description: Proxy mode
|
||
|
|
type: choice
|
||
|
|
choices:
|
||
|
|
- No proxy
|
||
|
|
- Auto-detect proxy settings for this network
|
||
|
|
- Use system proxy settings
|
||
|
|
- Manual proxy configuration
|
||
|
|
- Automatic proxy configuration URL
|
||
|
|
default: No proxy
|
||
|
|
mandatory: true
|
||
|
|
```
|
||
|
|
|
||
|
|
Cette variable nécessite une valeur (la valeur None n'est pas acceptable), elle est donc obligatoire (`mandatory`).
|
||
|
|
|
||
|
|
Si l'utilisateur ne précise pas de valeurs, la valeur de cette variable sera "No proxy" (`default`).
|
||
|
|
|
||
|
|
La variable est à choix (`type`: choice) donc la liste des valeurs disponible est contrainte (`choices`), seul les valeurs suivantes sont autorisés :
|
||
|
|
|
||
|
|
- No proxy
|
||
|
|
- Auto-detect proxy settings for this network
|
||
|
|
- Use system proxy settings
|
||
|
|
- Manual proxy configuration
|
||
|
|
- Automatic proxy configuration URL
|
||
|
|
|
||
|
|
Testons nos deux premiers dictionnaires :
|
||
|
|
|
||
|
|
```python
|
||
|
|
from rougail import Rougail, RougailConfig
|
||
|
|
from pprint import pprint
|
||
|
|
|
||
|
|
RougailConfig['dictionaries_dir'] = ['dict']
|
||
|
|
rougail = Rougail()
|
||
|
|
config = rougail.get_config()
|
||
|
|
config.property.read_only()
|
||
|
|
pprint(config.value.dict(), sort_dicts=False)
|
||
|
|
```
|
||
|
|
|
||
|
|
Donne :
|
||
|
|
|
||
|
|
```python
|
||
|
|
{'rougail.proxy.proxy_mode': 'No proxy'}
|
||
|
|
|
||
|
|
### Le mode manuel
|
||
|
|
|
||
|
|
Si l'utilisateur choisi le mode de proxy "Manual proxy configuration", on veut voir apparaitre (`disabled`) une nouvelle sous-famille appelé manual. Créons le fichier dict/02-proxy\_manual.yml
|
||
|
|
|
||
|
|
```yml
|
||
|
|
---
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
manual:
|
||
|
|
description: Manual proxy configuration
|
||
|
|
disabled:
|
||
|
|
type: jinja
|
||
|
|
jinja: |
|
||
|
|
{% if rougail.proxy.proxy_mode == 'Manual proxy configuration' %}
|
||
|
|
false
|
||
|
|
{% else %}
|
||
|
|
true
|
||
|
|
{% endif %}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### La configuration du proxy HTTP
|
||
|
|
|
||
|
|
Dans cette famille ajoutons une sous-famille `http_proxy` contenant les variables `http_address` et `http_port`. Créons le fichier dict/03-proxy\_manual\_http\_proxy.yml
|
||
|
|
|
||
|
|
```yml
|
||
|
|
---
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
manual:
|
||
|
|
http_proxy:
|
||
|
|
description: HTTP Proxy
|
||
|
|
http_address:
|
||
|
|
description: HTTP address
|
||
|
|
type: domainname
|
||
|
|
mandatory: true
|
||
|
|
http_port:
|
||
|
|
description: HTTP Port
|
||
|
|
type: port
|
||
|
|
default: '8080'
|
||
|
|
```
|
||
|
|
|
||
|
|
Les deux variables ont des types particulier (`domainname` ou `port`) pour valider les valeurs configurer par l'utilisateur.
|
||
|
|
|
||
|
|
#### Dupliquer la configuration HTTPS
|
||
|
|
|
||
|
|
On veux proposer à l'utilisateur la possiblité de renseigner le même proxy pour les requêtes HTTPS. Créons le fichier dict/04-proxy\_manual\_http\_use\_for\_https.yml :
|
||
|
|
|
||
|
|
```yml
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
manual:
|
||
|
|
use_for_https:
|
||
|
|
description: Also use this proxy for HTTPS
|
||
|
|
type: boolean
|
||
|
|
```
|
||
|
|
|
||
|
|
Cette variable est de type `boolean`. Sa valeur par défaut est `True`.
|
||
|
|
|
||
|
|
#### La configuration du proxy HTTPS
|
||
|
|
|
||
|
|
Ajoutons une nouvelle sous-famille `ssl\_proxy` avec les variables `ssl_address` et `ssl_port`. Créons le fichier dict/05-proxy\_manual\_ssl\_proxy.yml :
|
||
|
|
|
||
|
|
```yml
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
manual:
|
||
|
|
ssl_proxy:
|
||
|
|
description: HTTPS Proxy
|
||
|
|
hidden:
|
||
|
|
type: variable
|
||
|
|
variable: rougail.proxy.manual.use_for_https
|
||
|
|
ssl_address:
|
||
|
|
description: HTTPS address
|
||
|
|
type: domainname
|
||
|
|
default:
|
||
|
|
type: jinja
|
||
|
|
jinja: |
|
||
|
|
{% if rougail.proxy.manual.use_for_https %}
|
||
|
|
{{ rougail.proxy.manual.http_proxy.http_address }}
|
||
|
|
{% endif %}
|
||
|
|
mandatory: true
|
||
|
|
ssl_port:
|
||
|
|
description: HTTPS Port
|
||
|
|
type: port
|
||
|
|
default:
|
||
|
|
type: jinja
|
||
|
|
jinja: |
|
||
|
|
{% if rougail.proxy.manual.use_for_https %}
|
||
|
|
{{ rougail.proxy.manual.http_proxy.http_port }}
|
||
|
|
{% endif %}
|
||
|
|
mandatory: true
|
||
|
|
```
|
||
|
|
|
||
|
|
Suivant la valeur de la variable `use\_for\_https` cette famille apparaitra ou disparaitra. Contrairement à tout à l'heure, la famille n'est pas désactivé (`disabled`) parce que les variables devront rester accessible en mode lecture seule.
|
||
|
|
|
||
|
|
Testons ce point précis :
|
||
|
|
|
||
|
|
```python
|
||
|
|
>>> from rougail import Rougail, RougailConfig
|
||
|
|
>>> from pprint import pprint
|
||
|
|
>>>
|
||
|
|
>>> RougailConfig['dictionaries_dir'] = ['dict']
|
||
|
|
>>> rougail = Rougail()
|
||
|
|
>>> config = rougail.get_config()
|
||
|
|
>>> config.property.read_only()
|
||
|
|
>>> pprint(config.value.dict(), sort_dicts=False)
|
||
|
|
{'rougail.proxy.proxy_mode': 'No proxy'}
|
||
|
|
>>> config.property.read_write()
|
||
|
|
>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration')
|
||
|
|
>>> config.option('rougail.proxy.manual.http_proxy.http_address').value.set('proxy.example')
|
||
|
|
>>> pprint(config.value.dict(), sort_dicts=False)
|
||
|
|
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
|
||
|
|
'rougail.proxy.manual.http_proxy.http_address': 'proxy.example',
|
||
|
|
'rougail.proxy.manual.http_proxy.http_port': '8080',
|
||
|
|
'rougail.proxy.manual.use_for_https': True}
|
||
|
|
>>> config.property.read_only()
|
||
|
|
>>> pprint(config.value.dict(), sort_dicts=False)
|
||
|
|
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
|
||
|
|
'rougail.proxy.manual.http_proxy.http_address': 'proxy.example',
|
||
|
|
'rougail.proxy.manual.http_proxy.http_port': '8080',
|
||
|
|
'rougail.proxy.manual.use_for_https': True,
|
||
|
|
'rougail.proxy.manual.ssl_proxy.ssl_address': 'proxy.example',
|
||
|
|
'rougail.proxy.manual.ssl_proxy.ssl_port': '8080'}
|
||
|
|
```
|
||
|
|
|
||
|
|
Alors que la famille `rougail.proxy.manual` n'était pas présente lors que `rougail.proxy.proxy_mode` est à `No proxy`, la famille `rougail.proxy.manual.ssl_proxy` devient visible en mode lecture seule (et uniquement en mode lecture seule) si `rougail.proxy.manual.use\_for\_https` est à 'True'.
|
||
|
|
|
||
|
|
On voit bien également que les valeurs des variables de `rougail.http_proxy` on bien été copié dans `rougail.ssl_proxy`. Ces valeurs ne seront copié que si `use\_for\_https` est à "True" comme précisé dans les valeurs par défaut des variables.
|
||
|
|
|
||
|
|
#### La configuration du proxy SOCKS
|
||
|
|
|
||
|
|
Ajoutons une nouvelle sous-famille `socks\_proxy` avec les variables `socks\_address`, `socks\_port` et `socks\_version`. Créons le fichier dict/06-proxy\_manual\_socks\_proxy.yml :
|
||
|
|
|
||
|
|
```yml
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
manual:
|
||
|
|
socks_proxy:
|
||
|
|
description: SOCKS Proxy
|
||
|
|
socks_address:
|
||
|
|
description: SOCKS Address
|
||
|
|
type: domainname
|
||
|
|
socks_port:
|
||
|
|
description: SOCKS Port
|
||
|
|
type: port
|
||
|
|
socks_version:
|
||
|
|
description: SOCKS host version used by proxy
|
||
|
|
type: choice
|
||
|
|
choices:
|
||
|
|
- v4
|
||
|
|
- v5
|
||
|
|
default: v5
|
||
|
|
```
|
||
|
|
|
||
|
|
### Le mode détection automatique
|
||
|
|
|
||
|
|
Ajoutons une nouvelle sous-variable `auto`. Créons le fichier dict/07-proxy\_auto.yml :
|
||
|
|
|
||
|
|
```yml
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
auto:
|
||
|
|
type: web_address
|
||
|
|
description: Automatic proxy configuration URL
|
||
|
|
mandatory: true
|
||
|
|
disabled:
|
||
|
|
type: jinja
|
||
|
|
jinja: |
|
||
|
|
{% if rougail.proxy.proxy_mode == 'Automatic proxy configuration URL' %}
|
||
|
|
false
|
||
|
|
{% else %}
|
||
|
|
true
|
||
|
|
{% endif %}
|
||
|
|
```
|
||
|
|
|
||
|
|
Le type `web_address` impose une valeur qui commence par http:// ou https://.
|
||
|
|
|
||
|
|
## Les exceptions au proxy
|
||
|
|
|
||
|
|
Enfin ajoutons une variable contenant les exceptions au proxy. Pour cela créons le fichier dict/07-proxy\_no\_proxy.yml :
|
||
|
|
|
||
|
|
```yml
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
no_proxy:
|
||
|
|
description: Address for which proxy will be desactivated
|
||
|
|
multi: true
|
||
|
|
disabled:
|
||
|
|
type: jinja
|
||
|
|
jinja: |
|
||
|
|
{% if rougail.proxy.proxy_mode == 'No proxy' %}
|
||
|
|
true
|
||
|
|
{% else %}
|
||
|
|
false
|
||
|
|
{% endif %}
|
||
|
|
validators:
|
||
|
|
- type: jinja
|
||
|
|
jinja: '{{ rougail.no_proxy | valid_no_proxy }}'
|
||
|
|
```
|
||
|
|
|
||
|
|
Il peut y avoir plusieurs exceptions au proxy au proxy, la variable est donc `multi`.
|
||
|
|
Cette varible n'est pas accessible uniquement si aucun proxy n'est défini.
|
||
|
|
|
||
|
|
Enfin, on veut valider le contenu de la variable. Il n'y a pas de type correspond dans Rougail, donc on fait la fonction de validation suivante dans le fichier `functions.py` :
|
||
|
|
|
||
|
|
```python
|
||
|
|
from tiramisu import DomainnameOption
|
||
|
|
|
||
|
|
|
||
|
|
def valid_no_proxy(value: str):
|
||
|
|
try:
|
||
|
|
DomainnameOption('',
|
||
|
|
'',
|
||
|
|
value,
|
||
|
|
allow_ip=True,
|
||
|
|
allow_cidr_network=True,
|
||
|
|
allow_without_dot=True,
|
||
|
|
allow_startswith_dot=True,
|
||
|
|
)
|
||
|
|
except ValueError as err:
|
||
|
|
return err
|
||
|
|
```
|
||
|
|
|
||
|
|
Pour tester :
|
||
|
|
|
||
|
|
```python
|
||
|
|
from rougail import Rougail, RougailConfig
|
||
|
|
RougailConfig['functions_file'] = 'functions.py'
|
||
|
|
from pprint import pprint
|
||
|
|
|
||
|
|
RougailConfig['dictionaries_dir'] = ['dict']
|
||
|
|
rougail = Rougail()
|
||
|
|
config = rougail.get_config()
|
||
|
|
config.property.read_only()
|
||
|
|
pprint(config.value.dict(), sort_dicts=False)
|
||
|
|
```
|
||
|
|
|
||
|
|
## La demmande d'authentification
|
||
|
|
|
||
|
|
Rien de particulier à la création de la demande d'authentification. Pour cela créons le fichier dict/08-proxy\_prompt\_authentication.yml :
|
||
|
|
|
||
|
|
```yml
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
prompt_authentication:
|
||
|
|
description: Prompt for authentication if password is saved
|
||
|
|
type: boolean
|
||
|
|
default: true
|
||
|
|
disabled:
|
||
|
|
type: jinja
|
||
|
|
jinja: |
|
||
|
|
{% if rougail.proxy.proxy_mode == 'No proxy' %}
|
||
|
|
true
|
||
|
|
{% else %}
|
||
|
|
false
|
||
|
|
{% endif %}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Le DNS du proxy SOCKS v5
|
||
|
|
|
||
|
|
La question sur le DNS pour le proxy SOCKS v5 n'apparait que si le proxy est configuré et que la version du proxy SOCKS sélectionné est bien la "v5".
|
||
|
|
|
||
|
|
Créons le fichier dict.09-proxy\_proxy\_dns\_socks5.yml :
|
||
|
|
|
||
|
|
```yml
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
proxy_dns_socks5:
|
||
|
|
description: Use proxy DNS when using SOCKS v5
|
||
|
|
type: boolean
|
||
|
|
default: false
|
||
|
|
disabled:
|
||
|
|
type: jinja
|
||
|
|
params:
|
||
|
|
socks_version:
|
||
|
|
type: variable
|
||
|
|
variable: rougail.proxy.manual.socks_proxy.socks_version
|
||
|
|
propertyerror: false
|
||
|
|
jinja: |
|
||
|
|
{% if rougail.proxy.proxy_mode == 'No proxy' %}
|
||
|
|
true
|
||
|
|
{% elif socks_version is undefined or socks_version == 'v4' %}
|
||
|
|
true
|
||
|
|
{% else %}
|
||
|
|
false
|
||
|
|
{% endif %}
|
||
|
|
```
|
||
|
|
|
||
|
|
La difficulté c'est que la variable `rougail.proxy.manual.socks_proxy.socks_version` peut être désactivé (et donc non utilisable dans un calcul).
|
||
|
|
Dans ce cas, nous allons ajouter un paramètre (ici appelé `socks_version`) qui contiendra, s'il n'y a pas d'erreur de propriété, la valeur de la variable.
|
||
|
|
Sinon le paramètre ne sera pas passé au template Jinja. C'est pourquoi il faut tester dans le template Jinja si la variable `socks_version` existe bien.
|
||
|
|
|
||
|
|
### Le DNS à travers HTTPS
|
||
|
|
|
||
|
|
Enfin nous allons configurer le DNS à travers HTTPS dans le fichier 10-proxy\_dns\_over\_https.yml :
|
||
|
|
|
||
|
|
```yml
|
||
|
|
version: '1.0'
|
||
|
|
proxy:
|
||
|
|
dns_over_https:
|
||
|
|
description: DNS over HTTPS
|
||
|
|
enable_dns_over_https:
|
||
|
|
description: Enable DNS over HTTPS
|
||
|
|
type: boolean
|
||
|
|
default: false
|
||
|
|
provider:
|
||
|
|
description: Use Provider
|
||
|
|
type: choice
|
||
|
|
choices:
|
||
|
|
- Cloudflare
|
||
|
|
- NextDNS
|
||
|
|
- Custom
|
||
|
|
default: Cloudflare
|
||
|
|
disabled:
|
||
|
|
type: jinja
|
||
|
|
jinja: |
|
||
|
|
{% if rougail.proxy.dns_over_https.enable_dns_over_https %}
|
||
|
|
false
|
||
|
|
{% else %}
|
||
|
|
true
|
||
|
|
{% endif %}
|
||
|
|
custom_dns_url:
|
||
|
|
description: Custom DNS URL
|
||
|
|
type: web_address
|
||
|
|
mandatory: true
|
||
|
|
disabled:
|
||
|
|
type: jinja
|
||
|
|
params:
|
||
|
|
provider:
|
||
|
|
type: variable
|
||
|
|
variable: rougail.proxy.dns_over_https.provider
|
||
|
|
propertyerror: false
|
||
|
|
jinja: |
|
||
|
|
{% if provider is defined and provider == 'Custom' %}
|
||
|
|
false
|
||
|
|
{% else %}
|
||
|
|
true
|
||
|
|
{% endif %}
|
||
|
|
validators:
|
||
|
|
- type: jinja
|
||
|
|
jinja: |
|
||
|
|
{% if rougail.proxy.dns_over_https.custom_dns_url.startswith('http://') %}
|
||
|
|
only https is allowed
|
||
|
|
{% endif %}
|
||
|
|
```
|
||
|
|
|
||
|
|
La seule particularité ici c'est qu'on a ajouté une validation supplémentaire à la variable `custom_dns_url`. Seul une adresse commençant par https:// est autorisé (pas http://).
|