Solución | tls: handshake failure al renovar certificado SSL Let’s Encrypt con Cloudflare

Si al entrar en tu aplicación web aparece un mensaje del estilo:

IMPORTANT NOTES:
- The following errors were reported by the server:

Domain: test.com
Type: tls
Detail: remote error: tls: handshake failure

To fix these errors, please make sure that your domain name was
entered correctly and the DNS A/AAAA record(s) for that domain
contain(s) the right IP address. Additionally, please check that
you have an up-to-date TLS configuration that allows the server to
communicate with the Certbot client.
- The following errors were reported by the server:

Domain: www.test.com
Type: tls
Detail: remote error: tls: handshake failure

Y tu dominio está enlazado al server a través del CDN de cloudflare, ve a cloudflare.com, selecciona el dominio que da el error, accede al apartado crypto y selecciona ‘FULL SSL’.

Encontré este problema hace unos meses y me guardé la solución que, en su día me dio más de un quebradero de cabeza. Espero que te sea útil 😉

El grave error del taxi en España

Escribo estas líneas tras los sucesos colaterales a la huelga de taxistas acontecidos en ciudades de toda España. Me refiero a las agresiones generalizadas a conductores y vehículos que son, desde mi punto de vista, intolerables y un gran error que pienso, va a terminar de ahogar al sector del taxi. Este sector ya perdió buena parte de mi respeto cuando prendieron fuego a nueve coches de Cabify durante la feria de Sevilla del 2017, pero es que ahora siento que la situación se les ha ido totalmente de las manos a causa de una mala gestión y una falta total de innovación por parte del sector.

El último año lo pasé trabajando en Vietnam, donde operan dos grandes compañías de taxis tradicionales: Vinasun y Mailinh, además de dos ‘VTC’: Uber (que desapareció del sudeste asiático por lo que explico a continuación) y Grab, una especie de Uber con sede en Tailandia (lo que vendría a ser nuestro Cabify).

Cuando las VTC comenzaron a operar en Vietnam, trabajadores de estas dos compañías de taxi salieron a la calle a manifestarse, eso sí, con mayor civismo que sus hermanos Europeos. Como el país del que hablo tiene un gobierno del que no se puede esperar demasiada ayuda, las iniciativas a las que se llegaron fueron privadas; ambas compañías de taxi crearon sus propias apps ¡que en calidad nada tienen que envidiar a las de Uber, Grab o Cabify! Y, además, se aliaron con Grab (puedes pedir taxis convencionales desde esta app) lo cual provocó que Uber tuviera que dejar el continente pocos meses más tarde. Nadie perdió el trabajo y todos felices a día de hoy.

¿Si esto lo lograron hacer en Vietnam, por qué narices no somos capaces en España?

Ahí va la reflexión.

Calcular la altura de un árbol general en C++

El siguiente fragmento de código escrito en c++ calcula la altura de un árbol general de forma recursiva empleando plantillas de tipo T. Si bien esta pudiera no ser la forma más eficiente de realizar este procedimiento, es la que mejor escenifica el recorrido de la estructura de un árbol general. Tras el código he añadido un par de explicaciones, que se pueden completar con las dudas que dejes en los comentarios.

template <typename T>
template <typename T>
int Altura(const Agen<T> A, typename Agen<T>::nodo n){
    if( n == Agen<T>::NODO_NULO)
        return -1;
    else{
        // Tomamos al hijo de n
        typename Agen<T>::nodo hijo = A.hijoIzqdo(n);
        int max = -1;
        // Recorremos al mismo y a todos sus hermanos
        while(hijo != Agen<T>::NODO_NULO){
            int aux = Altura(A, hijo);
            if(aux > max)
                max = aux;
            hijo = A.hermDrcho(hijo);
        }
        return max + 1;
    }
}
}

A partir del nodo del árbol A, calculamos la altura, que se define como la longitud de la rama más larga que parte de n. Para aclararnos, la altura de un nodo hoja será 0 (-1 en el algoritmo) y la de la raíz será máxima. Si el nodo que encontramos es nulo, devuelve -1 para ajustar la solución, que de otra forma daría un entero de más. Si no, busca la máxima de las alturas entre el hijo izquierdo del nodo actual y su hermano derecho.

Archivos en MEDIA Django 1.11+ privados (requerir autenticación)

A menudo querremos ofrecer archivos privados en nuestra aplicación Django. La solución más sencilla a esta función es hacer una redirección vía regex a una función que lleve el decorador require_login, de la librería de Django.

Dicha redirección deberá ser incluida en el archivo urls.py principal de nuestro proyecto. Un ejemplo de implementación de esta funcionalidad es el siguiente:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

@login_required
def protected_serve(request, path, document_root=None, show_indexes=False):
    print("Serving " + path)
    return serve(request, path, document_root, show_indexes)

urlpatterns += [
    url( r'^%s(?P<path>.*)$'% settings.MEDIA_URL[1:], protected_serve, {'document_root': settings.MEDIA_ROOT}),
]

Modificando la ruta podrás distinguir qué archivos quedan privados y cuáles no. Si tengo tiempo escribiré un par de ejemplos. Gracias por leerme 😉

Fuente: http://blog.majsky.cz/django-protected-media-files/

Modificar plantilla de Administración en Django 1.11 + (1.8+)

El siguiente snippet pretende ilustrar de forma directa y sencilla cómo funciona el sistema de plantillas que Django incorpora desde la versión 1.8, aplicado a la sobre-escritura de la plantilla de administración. Para detalles técnicos está la fabula documentación de Django.

Comencemos creando una carpeta de nombre templates, dentro de esta otra llamada admin, y dentro de admin un archivo al que llamaremos base_site.html. La estructura deberá quedar como sigue:

proyecto
*proyecto
*app1
*app2
*appn
*templates
**admin
***base_site.html

A continuación entra en la carpeta de plantillas de administración de Django en Github: https://github.com/django/django/tree/master/django/contrib/admin/templates/admin

Dentro de este directorio busca el archivo base_site.html. Ábrelo, copia el código que haya en su interior y pégalo en el archivo base_site.html que creaste en el paso anterior.

{% extends "admin/base.html" %}

{% block title %}Título de la página{% endblock %}

{% block branding %}


&amp;amp;amp;lt;h1 id="site-name"&amp;amp;amp;gt;&amp;amp;amp;lt;a href="{% url 'admin:index' %}"&amp;amp;amp;gt;Cabecera de la página&amp;amp;amp;lt;/a&amp;amp;amp;gt;&amp;amp;amp;lt;/h1&amp;amp;amp;gt;


{% endblock %}

{% block nav-global %}{% endblock %}

Te quedará algo así, y aquí podrás modificar lo que tú quieras, pero para que los cambios aun se vean reflejados aun nos queda un paso más.

Entra en el archivo settings.py de tu proyecto y encuentra el bloque de código TEMPLATES. Dentro del diccionario DIRS, introduce la siguiente ruta:

os.path.join(BASE_DIR, ‘templates’),

Deberá quedar algo como:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },

]

Y ahora sí, al recargar tu página de administración podrás ver que los cambios han surtido efecto. A partir de esto, todo es probar a modificar lo que quieras a partir del proyecto Django original en GitHub.

Obtener coordenadas en un plano al hacer clic sobre él | Unity 5.6+

El siguiente snippet pretende ilustrar cómo se pueden obtener las coordenadas del mundo real al realizar un clic sobre la pantalla. Es válido para, al menos, las versiones de Unity 5.6 y superiores y está escrito en C#.

Cuando el usuario realiza una pulsación sobre la pantalla es, a priori, imposible determinar las coordenadas espaciales del punto donde pretendía pulsar, ya que para lograr esto se necesita una referencia. Es como si señaláramos a un cielo estrellado y dijéramos… ¡quiero las coordenadas de ese punto! Bien, ¿pero con respecto a qué?

En primer lugar debemos crear una referencia; en concreto un plano geométrico que nos servirá como base de operaciones. No confundas este plano con la forma geométrica primitiva de Unity que comparte su mismo nombre; este plano es una representación matemática de un plano en el espacio tridimensional. Puedes leer más acerca de los planos en Unity haciendo clic aquí, y sobre los planos geométricos aquí.

Una vez creado nuestro plano, plane, en la función update pedimos que cada vez que el usuario haga clic con el botón izquierdo del ratón, se cree un rayo (o semirrecta) geométrico que comience en el punto de la pantalla donde se ha realizado la pulsación. A continuación creamos una variable, rayDistance, que pasamos por referencia a la función plane.Raycast, la cuál creará la intersección entre nuestro rayo y el plano geométrico anteriormente creado. rayDistance será el resultado del cálculo de la longitud de la semirecta cuyo origen sea la posición del objeto que invoca a la función y el punto de intersección con el plano. Con éste último dato podemos obtener el punto del rayo geométrico situado a la recién calculada distancia. En nuestro caso, como prueba, creamos un cubo en el punto de choque.

Para entender el funcionamiento de este script lo mejor es hacer cuantos experimentos sean necesarios con el código de abajo, y también echarle un vistazo a la siguiente imagen con la que pretendo ilustrar el ejercicio.

  Plane plane = new Plane(Vector3.up, Vector3.zero);
	void Update () {
        if (Input.GetMouseButtonDown(0))
        {
          Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
          float rayDistance;
          if (plane.Raycast(ray, out rayDistance)) // Intersects a ray with the plane.
          {
            pivotPoint = ray.GetPoint(rayDistance);
            GameObject test = GameObject.CreatePrimitive(PrimitiveType.Cube);
            test.transform.position = pivotPoint;
          }
        }
      }

Registro de usuario en Django Rest Framework

Mi solución de registro de usuarios a través de una API rest. No he encontrado ejemplos completos en ninguna parte, y este código bien pudiera no ser óptimo.

#serializers.py
class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset that provides the standard actions
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = (AllowAny, )

    def create(self, request, *args, **kwargs):
        """
        http POST 127.0.0.1:8000/users/ By default asks for permission.
        :param request:
        :return:
        """
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid():
            user = serializer.create(validated_data=request.data)
            #serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#views.py
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        """
        Returns all the Avatars id related to one user. Must set the related_name from the many part.
        """
        avatars = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
        model = User
        fields = ('id', 'email', 'avatars', 'account_type', 'password', 'username')

    def create(self, validated_data):
        """
        We've to override the create serializer method in order to call to our custom create_user
        http://www.django-rest-framework.org/api-guide/serializers/#saving-instances
        :param validated_data: Incoming POST data
        :return: A created user instance
        """
        account_type = AccountType.objects.get(pk=validated_data['account_type'])
        email = validated_data['email']
        password = validated_data['password']
        username = validated_data['email']
        return User.objects.create_user(username=username, account_type=account_type, email=email, password=password) # Alternative: User.objects.create(**validated_data)

Contraseñas almacenadas en texto plano en Django Admin

Al configurar la interfaz de administración de nuestro usuario, sea personalizado o no, debemos heredar de la clase.

UserAdmin

Si simplemente implementamos la administración con una linea como la siguiente:

admin.site.register(User, CustomUserAdmin)

Al editar un nuevo usuario el sistema de cifrado de Django no procesará la petición y, por lo tanto, la contraseña quedará almacenada en texto plano en nuestra base de datos. Contraseña que, además, no servirá para autenticar al usuario ya que Django tratará de decodificar la contraseña que ya de por sí está decodificada en la base de datos para compararla con la introducida por el usuario.

Por suerte la solución es bien sencilla. Simplemente deja tu admins.py así:

from django.contrib.auth.admin import UserAdmin
class CustomUserAdmin(UserAdmin):
    pass

admin.site.register(User, CustomUserAdmin)

O si no quieres personalizar las opciones de tu usuario, así:

from django.contrib.auth.admin import UserAdmin

admin.site.register(User, UserAdmin)

Fecha de creación y edición automáticas en Django

En Django la fecha de creación y modificación no se añade por defecto a nuestros modelos, como sí ocurre en otros frameworks como Ruby On Rails. La forma más elegante de añadir esta funcionalidad a tus modelos es crear una clase base que herede de models.Model y luego hacer que todos tus modelos hereden de la misma.

class BaseModel(models.Model):
    created_date = models.DateTimeField(_('Date created'), auto_now_add=True)
    modified_date = models.DateTimeField(_('Date modified'), auto_now=True)

    class Meta:
        abstract = True

El resto de nuestros modelos donde queramos tener las fechas de creación y actualización automáticas deberán ser del tipo:

class AccountType(BaseModel):
    name = models.CharField(_('Name of this account type ie: Free'), blank=False, max_length=254)
    description = models.TextField(_('Explain the user what he can and can not do with this account'), blank=True, max_length=512)