Skip to content

38. Esercizio pratico: versione 18

38.1. Implementazione

Image

La cartella [impots/http-servers/13] viene inizialmente ottenuta copiando la cartella [impots/http-servers/12] e successivamente modificata parzialmente.

Iniziamo aggiungendo un nuovo parametro nel file [configs/parameters]:


…        
         # token CSRF
        "with_csrftoken"False,
        # database gestiti MySQL (mysql), PostgreSQL (pgres)
        "databases"["mysql""pgres"],
        # prefisso dell'applicazione URL
        # impostare la stringa vuota se non si desidera alcun prefisso, altrimenti /prefisso
        "prefix_url""/do",
        # URL radice del server Apache - impostare la stringa vuota per l'esecuzione al di fuori di Apache
        "application_root""/impots"

Alla riga 10, il parametro [application_root] rappresenterà l’alias WSGI del server virtuale Apache.

Con questo parametro, possiamo correggere l’istruzione di [responses/HtmlResponse] che aveva causato l’errore:

Image



# ora è necessario generare il URL di reindirizzamento senza dimenticare il token CSRF se richiesto
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""

        # risposta di reindirizzamento
        return redirect(f"{config['parameters']['application_root']}{config['parameters']['prefix_url']}{ads['to']}{csrf_token}")
, status.HTTP_302_FOUND
  • riga 9: abbiamo aggiunto la radice dell’applicazione all’inizio dell’URL destinazione del reindirizzamento;

Dobbiamo inoltre correggere tutti i frammenti in modo che i URL in essi contenuti inizino dalla radice dell’applicazione (o dall’alias WSGI):

Image

Il frammento [v-authentification]


<!-- modulo HTML - si inviano i valori con l'azione [authentifier-utilisateur] -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/authentifier-utilisateur{{modèle.csrf_token}}"
>

    <!-- titolo -->
    <div class="alert alert-primary" role="alert">
        <h4>Veuillez vous authentifier</h4>
    </div>


</form>

Il frammento [v-calcul-impot]


<!-- modulo HTML inviato -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/calculer-impot{{modèle.csrf_token}}">
    <!-- messaggio su 12 colonne su sfondo blu -->
    

</form>

Il frammento [v-liste-simulations]




{% if modèle.simulations is defined and modèle.simulations|length!=0 %}


<!-- tabella delle simulazioni -->
<table class="table table-sm table-hover table-striped">
    
    <tr>
        <th scope="row">{{simulation.id}}</th>
        <td>{{simulation.marié}}</td>
        <td>{{simulation.enfants}}</td>
        <td>{{simulation.salaire}}</td>
        <td>{{simulation.impôt}}</td>
        <td>{{simulation.surcôte}}</td>
        <td>{{simulation.décôte}}</td>
        <td>{{simulation.réduction}}</td>
        <td>{{simulation.taux}}</td>
        <td><a href="{{modèle.application_root}}{{modèle.prefix_url}}/supprimer-simulation/{{simulation.id}}{{modèle.csrf_token}}">Supprimer</a></td>
    </tr>
    {% endfor %}
    </tr>
    </tbody>
</table>
{% endif %}

Il frammento [v-menu]


<!-- menu Bootstrap -->
<nav class="nav flex-column">
    <!-- visualizzazione di un elenco di link HTML -->
    {% for optionMenu in modèle.optionsMenu %}
    <a class="nav-link" href="{{modèle.application_root}}{{modèle.prefix_url}}{{optionMenu.url}}{{modèle.csrf_token}}">{{optionMenu.text}}</a>
    {% endfor %}
</nav>

Tutti i frammenti sopra riportati utilizzano il modello [modèle.application_root]. Al momento la chiave [application_root] non esiste nei modelli generati dalle classi di modelli.

Image

La classe [AbstractBaseModelForView], che è la classe padre di tutte le classi che generano un modello, diventa la seguente:


from abc import abstractmethod

from flask import Request
from flask_wtf.csrf import generate_csrf
from werkzeug.local import LocalProxy

from InterfaceModelForView import InterfaceModelForView

class AbstractBaseModelForView(InterfaceModelForView):

    @abstractmethod
    def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
        pass

    def update_model(self, modèle: dict, config: dict):
        # calcolo del token CSRF
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""
        # aggiornamento del modello passato come parametro
        modèle.update({
            # token CSRF
            'csrf_token': csrf_token,
            # prefix_url
            'prefix_url': configQZXW2HTMLBWyJwYXJhbWV0ZXJzIl0ZQXQZXW2HTMLBWyJwcmVmaXhfdXJsIl0ZQX,
            # application_root
            'application_root': configQZXW2HTMLBWyJwYXJhbWV0ZXJzIl0ZQXQZXW2HTMLBWyJhcHBsaWNhdGlvbl9yb290Il0ZQX,
        })
  • riga 15: il metodo [update_model] ha il compito di inserire nel modello le viste:
    • riga 24: il token CSRF;
    • riga 26: il prefisso di URL;
    • riga 28: la radice dell’applicazione o alias WSGI;

Le quattro classi figlie richiamano la classe padre con il seguente codice:



        # azioni disponibili dalla vista
        modèle['actions_possibles'] = ["afficher-vue-authentification""authentifier-utilisateur"]

        # completamento del modello da parte della classe padre
        super().update_model(modèle, config)

        # si restituisce il modello
        return modèle
  • riga 6: ogni classe figlia chiama la propria classe padre per aggiornare il modello che ha creato;

La versione 18 è pronta. Riprendiamo i due server virtuali Apache della versione 17 e li modifichiamo:

Image

I due file [flask-impots-withXX.conf] vengono modificati solo in un punto:


# cartella dello script .wsgi
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/13/apache"

# nome del sito web configurato da questo file
# qui si chiamerà flask-impots-withmysql
# i URL saranno del tipo http(s)://flask-impots-withmysql/percorso
define SITE "flask-impots-withmysql"

# inserire l'indirizzo IP 127.0.0.1 per il sito SITE nel file c:/windows/system32/drivers/etc/hosts

# inserire qui i percorsi delle librerie Python da utilizzare - separarle con virgole
# qui le librerie di un ambiente Python virtuale
WSGIPythonPath  "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"

# Python Home - necessario solo se sono installate più versioni di Python
# WSGIPythonHome "C:/Program Files/Python38"

# URL HTTP
<VirtualHost *:80>
    # con l'alias / i URL avranno la forma /{prefixe_url}/action/...
    # con l'alias /impots, i URL avranno la forma /impots/{prefixe_url}/action/...
    # dove [prefixe_url] è definito in parameters.py
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    …
</VirtualHost>

# URL protette con HTTPS
<VirtualHost *:443>
    # con l'alias /, i URL avranno la forma /{prefixe_url}/action/...
    # con l'alias /impots, i URL avranno la forma /impots/{prefixe_url}/action/...
    # dove [prefixe_url] è definito in parameters.py
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    ..

</VirtualHost>
  • alla riga 12, ora si utilizza la cartella [impots/http-servers/13/apache].

Siamo pronti per i test della versione 18 su Apache. La configurazione è la seguente:

  • l’alias WSGI è /impots in entrambi i file di configurazione dei server virtuali;
  • nel file di configurazione [configs/parameters], i parametri sono i seguenti:

        # token CSRF
        "with_csrftoken"False,
        # prefisso dell'applicazione URL
        # impostare la stringa vuota se non si desidera alcun prefisso, altrimenti /prefisso
        "prefix_url""/do",
        # URL radice del server Apache - impostare la stringa vuota per l'esecuzione al di fuori di Apache
        "application_root""/impots"

Si avviano il server Apache e i due SGBD. Si richiede l’accesso a URL e [https://flask-impots-withmysql/impots/do]. La risposta del server è la seguente:

Image

Si ottiene correttamente la pagina di autenticazione che non era stato possibile visualizzare nella versione precedente. Il resto dell’applicazione funziona normalmente.

Ora si testa l’altro server virtuale. Si richiedono i codici URL e [https://flask-impots-withpgres/impots/do]. La risposta del server è la seguente:

Image

I concetti di alias WSGI e prefisso URL svolgono la stessa funzione. Uno di questi due concetti è ridondante. Pertanto, per anteporre la stringa [/impots/do] ai URL del server Apache, è possibile procedere in tre modi:

1 – [WGSIAlias /impots] e [prefix_url=’/do’];

2 - [WGSIAlias /] e [prefix_url=’/impots/do’];

3 - [WGSIAlias /impots/do] e [prefix_url=’’];

38.2. Test da console

Si utilizzano nuovamente i test da console del client [http-clients/09]:

Image

  • il URL del server deve essere modificato nelle configurazioni [1] e [3];
  • è necessario apportare una modifica al livello [dao] affinché supporti il protocollo HTTPS del server Apache;

Nei file [config], il valore URL del server diventa il seguente:


        "server": {
            # "urlServer": "http://127.0.0.1:5000",
            # "urlServer": "http://127.0.0.1:5000/do",
            "urlServer""https://flask-impots-withmysql/impots/do",
            "user": {
                "login""admin",
                "password""admin"
            },
            "url_services": {
                
            }
        },
        # modalità debug
        "debug"True,
        # csrf_token
        "with_csrftoken"False,
  • riga 4: il nuovo URL del server. Per la prima volta in questo documento, il client utilizza il protocollo HTTPS;

La classe [ImpôtsDaoWihHttpSession] del livello [dao] si evolve come segue:


…    
     # fase richiesta/risposta
    def get_response(self, method: str, url_service: str, data_value: dict = None, json_value=None):
        # [method]: metodo HTTP GET o POST
        # [url_service]: URL di servizio
        # [data]: parametri di POST in formato x-www-form-urlencoded
        # [json]: parametri di POST in formato JSON
        # [cookies]: cookie da includere nella richiesta

        # è necessario disporre di una sessione XML o JSON, altrimenti non sarà possibile gestire la risposta
        if self.__session_type not in ['json''xml']:
            raise ImpôtsError(73"il n'y a pas de session valide en cours")

        # si aggiunge il token CSRF a URL di servizio
        if self.__csrf_token:
            url_service = f"{url_service}/{self.__csrf_token}"

        # esecuzione della richiesta
        response = requests.request(method,
                                    url_service,
                                    data=data_value,
                                    json=json_value,
                                    cookies=self.__cookies,
                                    allow_redirects=True,
                                    # per il protocollo https
                                    verify=False)

        # modalità debug?
        if self.__debug:
            # utente
            if not self.__logger:
                self.__logger = self.__config['logger']
            # si effettua l'accesso
            self.__logger.write(f"{response.text}\n")

        

        # si restituisce il risultato
        return résultat['réponse']
  • alla riga 26, si aggiunge il parametro [verify=False] a causa del protocollo HTTPS utilizzato dal server Apache. Il modulo [requests] (riga 19) è in grado di gestire in modo nativo il protocollo HTTPS. Per impostazione predefinita, verifica la validità del certificato di sicurezza inviato dal server HTTPS e genera un'eccezione se il certificato ricevuto non è valido. È proprio questo il caso in cui il server Apache di Laragon invia un certificato autofirmato. Per evitare l’eccezione, si utilizza il parametro [verify=False] per indicare al modulo [requests] di non generare un’eccezione. [requests] si limita quindi a visualizzare un avviso (warning) sulla console.

Una volta apportate queste modifiche, tutti i test da console dovrebbero funzionare correttamente.