MVC sobre CGI en Python


Autor: Eugenia Bahit

Resumen

Este documento está basado en el paper del proyecto «MVCGI: Modelo de Implementación Estándar para arquitecturas MVC modulares en programas de interfaz CGI» publicado en diciembre de 2016 , en el cual se propone un modelo arquitectónico para la implementación de arquitecturas MVC modulares en programas CGI, centrado en la seguridad.

 

El objetivo de este modelo se encuentra en:

  • La agilización del sistema, por reducción de sobrecarga de procesos
  • La seguridad de la aplicación, mediante la implementación de un modelo teórico hipotético

 

Previa contextualización, se expone el procedimiento completo para poder replicarlo un proyecto MVC en Python, bajo el servidor http de Apache con una interfaz CGI.

 

Contexto

CGI (Common Gateway Interfaz) es definido en la RFC3875 como una interfaz sencilla para ejecutar programas bajo servidores HTTP de forma independiente a la plataforma (Robinson & Coard, 2004), proponiendo un mecanismo de comunicación entre la aplicación y el servidor que facilita la independencia de ambos.

 

Por su parte, MVC es un patrón arquitectónico, originalmente definido como un «lenguaje de patrones» (Reenskaug, 1979) y posteriormente como «paradigma de programación» (Krasner & Pope, 1988), que propone una independencia similar entre la abstracción de los datos de un sistema y las diferentes representaciones gráficas de éstos (Reenskaug, 1979).

 

La independencia que plantea la implementación simultánea de CGI como interfaz de comunicación entre la aplicación y el servidor, por un lado, y, MVC como método de comunicación interno de los componentes abstractos de un programa y su interfaz gráfica de usuario (GUI), por el otro, podrían suponer la posibilidad de crear herramientas de bajo coste para el servidor (debido a la escasez de recursos que requieren ser consumidos) y de gran portabilidad (como consecuencia directa de la falta de dependencia).

 

En los años posteriores a la presentación de los modelos ut supra mencionados (e incluso en la actualidad) se han presentado un gran número de herramientas destinadas al desarrollo de sistemas informáticos y de a poco, la interfaz CGI ha ido quedando en desuso y con ella, las investigaciones sobre la seguridad en programas que implementen dicha interfaz. Por otra parte, el scripting involucrado en la escritura de programas CGI, no ha sido incluido hasta la actualidad, como parte de los estudios de investigación sobre buenas prácticas de programación y estructuras de código limpio, siendo ambas problemáticas, seguridad y falta de estudio de las buenas prácticas en el scripting, dos posibles impedimentos para considerar viable el diseño de programas CGI que implementen arquitecturas MVC modulares, seguras, portables, livianas y legibles.

 

El modelo MVCGI surge como alternativa viable para la erradicación de ambas problemáticas, ofreciendo una propuesta para la implementación de arquitecturas MVC modulares en programas CGI.


Procedimiento

1. Creación del File System

MVCGI propone la siguiente estructura de archivos y directorisos (file system):

rootsystem/ 
├── [drwxr-xr-x] application/ 
│   ├── [drwxr-xr-x] core/ 
│   │   └── [drwxr--r--] __init__.py
│   ├── [drwxr-xr-x] modules/
│   │   └── [drwxr--r--] __init__.py
│   ├── [-rw-r--r--] __init__.py
│   ├── [-rw-r--r--] config.py
│   ├── [-rwxr-xr-x] xfc.py*
│   └── [-rw-r--r--] settings.py
└── [drwxr-xr-x] static/

# Directorios opcionales (fuera de rootsystem)
[drwxr-xr-x] logs/
[drwxrwxrwx] private/

 

Breve explicación de la función de cada componente del File System

  • Directorio raíz del sistema
    Destinado a nuclear los archivos estáticos y de la aplicación.
     
  • Directorio de aplicación:
    Destinado a nuclear los archivos fuente de la aplicación y mantenerlos separados del contenido estático.
     
  • Núcleo del sistema
    Directorio destinado a nuclear los archivos centrales de la aplicación, comunes a cualquier sistema.
     
  • Módulos del sistema
    Directorio destinado al almacenaje de archivos de funcionalidades específica de la aplicación.
     
  • Archivo de control frontal ejecutable
    (eXecutable Front Controller - XFC) Manejador general de solicitudes HTTP de la aplicación.
     
  • Archivo de inicialización de variables de entorno
    Meta-variables (o variables de entorno) de la aplicación, necesarias para su funcionamiento pero no configurables.
     
  • Archivo de configuración
    Variables de configuración específica de la aplicación.
     
  • Directorio estático
    Destinado a nuclear archivos estáticos y binarios no ejecutables.

 

1.1 Creación del archivo de configuración application/config.py
DEFAULT_RESOURCE = "/modulo/recurso"  # Módulo y recurso por defecto
SHOW_ERROR_404 = False

 

1.2 Creación del archivo de inicialización de variables application/settings.py
from os import environ

from config import *


URL_LIST = environ['REQUEST_URI'][1:].split('/')
MODULE = URL_LIST[0].replace('.', '')
PACKAGE = "modules.{}".format(MODULE)
MODULE_PATH = "{}.py".format(PACKAGE.replace('.', '/'))
CONTROLLER = "{}Controller".format(MODULE.title())
RESOURCE = URL_LIST[1] if len(URL_LIST) > 1 else ''
ARG = URL_LIST[2] if len(URL_LIST) > 2 else 0

HTTP_404 = "Status: 404 Not Found\n"
HTTP_HTML = "Content-type: text/html; charset=utf-8\n"
HOST = "http://{}".format(environ['SERVER_NAME'])
HTTP_REDIRECT = "Location: {}{}\n".format(HOST, DEFAULT_RESOURCE)

 

1.3 Creación del archivo ejecutable application/xfc.py
#!/usr/bin/env python
#-*- coding: utf-8 -*-

from os.path import isfile

from settings import MODULE_PATH, PACKAGE, CONTROLLER, RESOURCE, HTTP_404,\
    HTTP_HTML, SHOW_ERROR_404, HTTP_REDIRECT, MODULE

error_module = error_resource = True

if isfile(MODULE_PATH):
    modulo = __import__(PACKAGE, fromlist=[CONTROLLER])
    controller = getattr(modulo, CONTROLLER)()
    error_module = False

if not error_module and hasattr(controller, RESOURCE):
    getattr(controller, RESOURCE)()
    error_resource = False

if error_module or error_resource:
    error_header = "{}{}\n".format(HTTP_404, HTTP_HTML)
    print error_header if SHOW_ERROR_404 else HTTP_REDIRECT
            

 

2. Creación del VirtualHost

Archivo: /etc/apache2/sites-available/<hostname>.conf

<VirtualHost *:80>
    ServerName <hostname>.local
    DocumentRoot /<project-path>/rootsystem
    ErrorLog /<project-path>/logs/error.log
    CustomLog /<project-path>/logs/access.log combined

    <Directory "/<project-path>/rootsystem">
        Options -Indexes
        AllowOverride None
        RewriteEngine On
        RewriteRule !(^static) application/xfc.py
    </Directory>

    <Directory "/<project-path>/rootsystem/application">
        Options +ExecCGI -Indexes
        <FilesMatch "\.py$">
            setHandler cgi-script
        </FilesMatch>
    </Directory>

    <Directory "/<project-path>/rootsystem/static">
        Options -Indexes
        AllowOverride None
    </Directory>
</VirtualHost> 

 

3. Configuraciones del servidor http de Apache

3.1. Habilitar VirtualHost

a2ensite <hostname>.conf

 

3.2. Habilitar módulo cgi

a2enmod cgi

 

3.2.1 Si lo anterior falla, deshabilitar worker y habilitar prefork

a2dismod mpm_worker && service apache2 restart

a2enmod mpm_prefork

 

3.3. Reiniciar Apache

service apache2 restart

 

4. Habilitación del nuevo host

echo "127.0.0.1 <hostname>.local" >> /etc/hosts

 

5. Creación de un módulo de prueba

Archiv: application/modules/modulo.py

from settings import HTTP_HTML


class Modulo(object):
    pass


class ModuloView(object):

    def vista(self):
        print HTTP_HTML
        print ""
        print "Hola Mundo"


class ModuloController(object):
    
    def __init__(self):
        self.model = Modulo()
        self.view = ModuloView()
    
    def recurso(self):
        self.view.vista()

 

6. Prueba

Ingresar en la URL: http://<hostname>.local/modulo/recurso y comprobar que la salida sea Hola mundo

 

Política de nombres

Se utiliza como nombre de módulo el nombre del modelo. Se utilizan el nombre del modelo con los sufijos “View” y “Controller” para crear las vistas y controladores respectivamente. Ejemplo:

Modelo:         Producto
Vista:          ProductoView
Controlador:    ProductoController
Helper:         ProductoHelper (opcional)
Módulo:         application/modules/producto.py 
URI:            /producto/<recurso>

 

Obtener el paper completo

© EUGENIA BAHIT 2015, 2016, 2017 - CC-BY 4.0
La copia y distribución de este documento se encuentra permitida bajo los términos de la licencia Bajo licencia Creative Commons Atribución 4.0