Odoo y los CORS

Soluciones y Experiencias con los CORS en Odoo

Ignacio Buioli
- 11/09/2019 - 9 min. de lectura

Internet ya ha cumplido sus buenos 30 años. Lejos quedó esa idea inicial de compartir simplemente artículos científicos, más cercano en el tiempo fueron los años de la web anónima y descontrolada, sin ningún criterio estético, incluso el poderoso Flash hoy es un mero recuerdo. Pero con todo lo bueno llegan un sin fin de problemas o, mejor dicho, auténticos quebraderos de cabeza. Al no ser una herramienta concebida para nada de lo que la usamos hoy, la seguridad en internet ha sido siempre poco más que un chiste. Poco a poco hemos mejorado en este aspecto, hoy día el protocolo HTTP está en peligro de extinsión por la casi obligación de utilizar HTTPS (casi ninguna API permite conexión, los motores de búsqueda no indexan con tanta facilidad, etc); y, por supuesto, ahora tenemos los CORS. ¡Claro que sí! ¡Los CORS! Aquella tecnología que nos permite "controlar" los recursos cruzados. Y no me malinterpreten, es una gran herramienta para bloquear pedidos de recursos a terceros, nos dan una capa de seguridad muy poderosa. Tan buena es, que muchas veces nos bloquean nuestras propias Apps.

Odoo no es la excepción. Con el correr de las versiones es muy dificil encontrarse con clientes dispuestos a actualizar Odoo cada año, lo harán eventualmente pero aún hay toneladas de empresas muy cómodas en Odoo 8. Sus plataformas funcionan para la labor que las necesitan, nada de lo nuevo de Odoo los encandila como para migrar, ¿para qué migrar entonces? Y gracias a que con Odoo pueden crearse Apps e Interfaces mediante conexión por XMLRPC, la verdad es que suele optarse por esto a la hora de invertir en desarrollo. XMLRPC está muy bien, lo que ocurre es que al hacer una App (con Angular por ejemplo) es muy probable encontrarnos con el siguiente error en la consola JavaScript:

Access to XMLHttpRequest at ' ' from origin ' ' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

¡Estoy intentando conectarme a mi propio servidor desde una App local! ¡Mi usuario y contraseña son necesarios! ¡Qué más da la política cruzada! Así es, suele ser desesperante, y en cierta manera incomprensible. Pero tiene su razón, el navegador no tiene idea de que recurso estamos pidiendo. Para nosotros la lógica es que la seguridad está garantizada ya que las credenciales (nombre de DB, usuario, contraseña) se cargan a mano en la App; pero los CORS solo ven un servidor remoto queriendo acceder a un servidor diferente, cuya configuración no permite compartir recursos cruzados.

Solución a los CORS (Odoo 8 y posiblemente Odoo 9)

Las versiones más antiguas de Odoo no tenian dicho problema porque los CORS no estaban definidos del todo. Hoy se encuentran como versiones superadas por una internet que avanza rápido. Sin embargo, la solución es tan "simple" como agregar los Headers de los CORS al server. Hay varias formas de hacer esto, suele recomendarse ubicarlos en Nginx pero no siempre nuestra configuración lo permite. Lo mejor es modificar archivos de Odoo (algo que no es recomendable, pero al tratarse de versiones que ya no se actualizan no importa tanto). Deberemos dirigirnos, en el root de Odoo, a openerp/service/wsgi_server.py y modificar las siguientes funciones:

def xmlrpc_return(start_response, service, method, params, string_faultcode=False):
    try:
        result = openerp.http.dispatch_rpc(service, method, params)
        response = xmlrpclib.dumps((result,), methodresponse=1, allow_none=False, encoding=None)
    except Exception, e:
        if string_faultcode:
            response = xmlrpc_handle_exception_string(e)
        else:
            response = xmlrpc_handle_exception_int(e)
    start_response("200 OK", [
    ('Content-Type','text/xml'), ('Content-Length', str(len(response))),
    ('Access-Control-Allow-Origin','*'),
    ('Access-Control-Allow-Methods','POST, GET, OPTIONS'),
    ('Access-Control-Max-Age',1000),
    ('Access-Control-Allow-Headers','origin, x-csrftoken, content-type, accept'),    ])
    return [response]

def wsgi_xmlrpc(environ, start_response):
    if environ['REQUEST_METHOD'] == "OPTIONS":
        response = werkzeug.wrappers.Response('OPTIONS METHOD DETECTED')
        response.headers['Access-Control-Allow-Origin'] = '*'
        response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
        response.headers['Access-Control-Max-Age'] = 1000
        response.headers['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept'
        return response(environ, start_response)
    if environ['REQUEST_METHOD'] == 'POST' and environ['PATH_INFO'].startswith('/xmlrpc/'):
        length = int(environ['CONTENT_LENGTH'])
        data = environ['wsgi.input'].read(length)
        string_faultcode = True
        if environ['PATH_INFO'].startswith('/xmlrpc/2/'):
            service = environ['PATH_INFO'][len('/xmlrpc/2/'):]
            string_faultcode = False
        else:
            service = environ['PATH_INFO'][len('/xmlrpc/'):]
        params, method = xmlrpclib.loads(data)
        return xmlrpc_return(start_response, service, method, params, string_faultcode)


Lo que está en verde debe ser agregado. Guardar y reiniciar el server.

Otros errores de CORS

Desde ya, este no es ni mucho menos el único error que nos va a salir con los CORS. Aunque en defensa de Odoo, muchos errores son producto de una mala configuración del servidor. Uno de ellos está relacionado a este mensaje de error en el log de la consola:

Access-Control-Allow-Origin: '*, *'

Este de por sí extraño error se caracteriza por indicarnos que el servidor acepta recursos cruzados de *, *. Como sabemos, el signo de * lo utilizamos para indicar que todos los accesos son permitidos. Entonces, ¿qué significa *, *? Algo tan simple como que estamos diciendole dos veces (mediante plataformas diferentes) que soporta dichos accesos. Es extraño, lo lógico es que el * sobreescriba todo, pero CORS no funciona así. En este caso recomiendo revizar la configuración de Nginx o Apache en búsca del header Access-Control-Allow-Origin, ya que si lo llamamos desde ahí y a su vez desde el archivo wsgi_server.py de Odoo, será el responsable del error. El header Access-Control-Allow-Origin solo debe utilizarse una única vez.

Recordar que los navegadores actuales utilizan un CORS incluso en apps locales que estamos desarrollando. Por lo tanto, además de tener el server con los permisos configurados, necesitamos que el navegador no nos bloquee. Cuando la App se compila y se ejecuta de manera local este problema desaparece, pero mientras estamos desarrollando es aconsejable desactivar la seguridad del navegador o utilizar algún plugin que deshabilite los CORS (los hay para Firefox y para Chrome).

Acerca de:

Ignacio Buioli

Licenciado en Artes Multimediales. Ha desarrollado numerosos proyectos de Multimedia así como también escrito artículos y traducido textos del mencionado tema. En Moldeo Interactive es Socio y Programador; encargándose, además, de gran parte de las redes y los cursos online.