Skip to content

38. Exercício prático: Versão 18

38.1. Implementação

Image

A pasta [impots/http-servers/13] é inicialmente criada através da cópia da pasta [impots/http-servers/12] e, em seguida, parcialmente modificada.

Começamos por adicionar um novo parâmetro ao ficheiro [configs/parameters]:


…        
        # token csrf
        "with_csrftoken"False,
        # bases gérées MySQL (mysql), PostgreSQL (pgres)
        "databases"["mysql""pgres"],
        # préfixe des URL de l'application
        # mettre la chaîne vide si on ne veut pas de préfixe ou /préfixe sinon
        "prefix_url""/do",
        # url racine du serveur Apache - mettre la chaîne vide pour une exécution en-dehors d'Apache
        "application_root""/impots"

Linha 10: O parâmetro [application_root] representará o alias WSGI do servidor virtual Apache.

Com este parâmetro, podemos corrigir a diretiva [responses/HtmlResponse] que causou o erro:

Image



# now it's time to generate the URL redirection, not forgetting the CSRF token if requested
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""
 
        # redirect response
        return redirect(f"{config['parameters']['application_root']}{config['parameters']['prefix_url']}{ads['to']}{csrf_token}")
, status.HTTP_302_FOUND
  • linha 9: adicionámos a raiz da aplicação ao início do URL de destino do redirecionamento;

Também precisamos de corrigir todos os fragmentos para que as URLs que contêm comecem com a raiz da aplicação (ou alias WSGI):

Image

O fragmento [v-authentication]


<!-- form HTML - post its values with the [authenticate-user] action -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/authentifier-utilisateur{{modèle.csrf_token}}"
>
 
    <!-- title -->
    <div class="alert alert-primary" role="alert">
        <h4>Veuillez vous authentifier</h4>
    </div>

 
</form>

O fragmento [v-calcul-impot]


<!-- form HTML posted -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/calculer-impot{{modèle.csrf_token}}">
    <!-- 12-column message on blue background -->
    
 
</form>

O fragmento [v-liste-simulations]



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

 
<!-- simulation table -->
<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 %}

O fragmento [v-menu]


<!-- bootstrap menu -->
<nav class="nav flex-column">
    <!-- display a list of links 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>

Todos os fragmentos acima utilizam o modelo [model.application_root]. Atualmente, a chave [application_root] não existe nos modelos gerados pelas classes de modelo.

Image

A classe [AbstractBaseModelForView], que é a classe pai de todas as classes que geram um modelo, passa a ter a seguinte forma:

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):
        #  token calculation CSRF
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""
        #  update the model passed in parameter
        modèle.update({
            #  csrf token
            'csrf_token': csrf_token,
            #  prefix_url
            'prefix_url': config["parameters"]["prefix_url"],
            #  application_root
            'application_root': config["parameters"]["application_root"],
        })
  • linha 15: o método [update_model] é responsável por adicionar o seguinte ao modelo de visualização:
    • linha 24: o token CSRF;
    • linha 26: o prefixo da URL;
    • linha 28: a raiz da aplicação ou o alias WSGI;

As quatro classes filhas chamam a classe pai com o seguinte código:



        # actions possibles à partir de la vue
        modèle['actions_possibles'] = ["afficher-vue-authentification""authentifier-utilisateur"]
 
        # finition du modèle par la classe parent
        super().update_model(modèle, config)
 
        # on rend le modèle
        return modèle
  • linha 6: cada classe filha chama a sua classe pai para atualizar o modelo que criou;

A versão 18 está pronta. Reutilizamos os dois servidores virtuais do Apache da versão 17 e modificamo-los:

Image

Os dois ficheiros [flask-impots-withXX.conf] são modificados num único local:


# dossier du script .wsgi
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/13/apache"
 
# nom du site web configuré par ce fichier
# ici il s'appellera flask-impots-withmysql
# les URL seront du type http(s)://flask-impots-withmysql/path
define SITE "flask-impots-withmysql"
 
# mettre l'adresse IP 127.0.0.1 pour site SITE dans c:/windows/system32/drivers/etc/hosts
 
# mettre ici les chemins des bibliothèques Python à utiliser - les séparer par des virgules
# ici les bibliothèques d'un environnement virtuel Python
WSGIPythonPath  "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"
 
# Python Home - nécessaire uniquement s'il y a plusieurs versions de Python installées
# WSGIPythonHome "C:/Program Files/Python38"
 
# URL HTTP
<VirtualHost *:80>
    # avec l'alias / les URL auront la forme /{prefixe_url}/action/...
    # avec l'alias /impots les URL auront la forme /impots/{prefixe_url}/action/...
    # où [prefixe_url] est défini dans parameters.py
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    …
</VirtualHost>
 
# URL sécurisées avec HTTPS
<VirtualHost *:443>
    # avec l'alias / les URL auront la forme /{prefixe_url}/action/...
    # avec l'alias /impots les URL auront la forme /impots/{prefixe_url}/action/...
    # où [prefixe_url] est défini dans parameters.py
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    ..
 
</VirtualHost>
  • Linha 12: Estamos agora a utilizar o diretório [taxes/http-servers/13/apache].

Estamos prontos para testar a versão 18 a funcionar no Apache. A configuração é a seguinte:

  • o alias WSGI é /impots em ambos os ficheiros de configuração do servidor virtual;
  • no ficheiro de configuração [configs/parameters], os parâmetros são os seguintes:

        # token csrf
        "with_csrftoken"False,
        # préfixe des URL de l'application
        # mettre la chaîne vide si on ne veut pas de préfixe ou /préfixe sinon
        "prefix_url""/do",
        # url racine du serveur Apache - mettre la chaîne vide pour une exécution en-dehors d'Apache
        "application_root""/impots"

Iniciamos o servidor Apache e os dois SGBDs. Solicitamos a URL [https://flask-impots-withmysql/impots/do]. A resposta do servidor é a seguinte:

Image

Conseguimos aceder à página de autenticação, à qual não tínhamos acesso na versão anterior. O resto da aplicação funciona normalmente.

Agora testamos o outro servidor virtual. Solicitamos a URL [https://flask-impots-withpgres/impots/do]. A resposta do servidor é a seguinte:

Image

Os conceitos de aliases WSGI e prefixos de URL têm a mesma finalidade. Um destes dois conceitos é redundante. Assim, para prefixar as URLs do servidor Apache com a cadeia [/impots/do], existem três formas de o fazer:

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

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

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

38.2. Testes de consola

Utilizamos novamente os testes da consola do cliente [http-clients/09]:

Image

  • a URL do servidor deve ser modificada nas configurações [1] e [3];
  • deve ser feita uma alteração na camada [dao] para que esta suporte o protocolo HTTPS do servidor Apache;

Nos ficheiros [config], a URL do servidor passa a ser a seguinte:


        "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": {
                
            }
        },
        # mode debug
        "debug"True,
        # csrf_token
        "with_csrftoken"False,
  • linha 4: o novo URL do servidor. Pela primeira vez neste documento, o cliente utiliza o protocolo HTTPS;

A classe [ImpôtsDaoWihHttpSession] na camada [dao] evolui da seguinte forma:

…    
    # étape request / response
    def get_response(self, method: str, url_service: str, data_value: dict = None, json_value=None):
        #  [method]: HTTP GET or POST method
        #  [url_service] : URL of service
        #  [data]: POST parameters in x-www-form-urlencoded
        #  [json]: POST parameters in json
        #  [cookies]: cookies to include in the request

        #  you must have a XML or JSON session, otherwise you won't be able to handle the response
        if self.__session_type not in ['json', 'xml']:
            raise ImpôtsError(73, "il n'y a pas de session valide en cours")

        #  we add the CSRF token to the URL service token
        if self.__csrf_token:
            url_service = f"{url_service}/{self.__csrf_token}"

        #  query execution
        response = requests.request(method,
                                    url_service,
                                    data=data_value,
                                    json=json_value,
                                    cookies=self.__cookies,
                                    allow_redirects=True,
                                    #  for the https protocol
                                    verify=False)

        #  debug mode?
        if self.__debug:
            #  logger
            if not self.__logger:
                self.__logger = self.__config['logger']
            #  log on
            self.__logger.write(f"{response.text}\n")

        …

        #  we return the result
        return résultat['réponse']
  • Na linha 26, adicionamos o parâmetro [verify=False] devido ao protocolo HTTPS utilizado pelo servidor Apache. O módulo [requests] (linha 19) suporta nativamente o protocolo HTTPS. Por predefinição, este verifica a validade do certificado de segurança enviado pelo servidor HTTPS e lança uma exceção se o certificado recebido for inválido. É o que acontece aqui, onde o servidor Apache do Laragon envia um certificado autoassinado. Para evitar a exceção, usamos o parâmetro [verify=False] para indicar ao módulo [requests] que não lance uma exceção. O [requests] limita-se então a apresentar um aviso na consola.

Com estas alterações feitas, todos os testes de consola devem funcionar.