publikaccion @publikaccion

martes, mayo 29, 2007

.: LDAP tilde/s, eñe/s Latin-1 (ISO-8859-1), UTF-8 y Base 64

Estos días el LDAP me ha estado dando la lata con el tema de las tildes, las eñes y otras hierbas del Latin-1 (ISO-8859-1) que es el estándar que reconoce nuestros caracteres acentuados, eñes y cedillas, y demás símbolos latinos escritos.

Necesitaremos:

- OpenLDAP
- OpenSSL

No entro en detalle de cómo se instala ninguno de estos 2 productos, ya que considero que hay suficiente información en sus respectivas webs para que cada uno sea el que se encargue de dichas instalaciones en sus máquinas y sistemas operativos correspondientes.

El caso es que intentando introducir una nueva entrada o NODO en un OpenLDAP, me encontraba con que cada vez que introducía un CN (Common Name) con acento através del formato LDIF de datos paa LDAP, el slapd me devolvía un error indicando que el DN estaba mañ formato (DN que por supuesto tenía tilde), sin embargo cuando daba de alta el mismo de DN mediante una herramienta visual como es JXplorer, el campeón se tragaba los acentos sin ningún tipo de queja, y eso me llevó a mosquearme, ya que el formato texto de LDIF no me dejaba.

En el ejemplo (ficticio para más información), tenía un caso del tipo:

dn: cn=Javier Gómez García,ou=people,dc=dominio,dc=es
objectClass: top
objectClass: usuario
cn: Javier Gómez García
uid: jgomezg
userName: jgomezg


al final encontré el por qué no podemos hacer una exportación en limpio desde línea de comandos con un LDIF que generemos nosootros.

La explicación viene por el siguiente motivo, principalmente el LDAP funciona con codificación de caracteres UTF-8, no en Latin-1 que es la iso-8859-1 que reconoce los caracteres españoles como tildes, diéresis, eñes o cedillas, de modo que intentar meter desde un LDIF un acento en un campo que tenga reconocimiento específico de caracteres (algunos atributos permiten introducir acentos sin problemas), causa que nos dé un error de sintaxis en el DN, en el CN, o en donde se encuentre las tildes codificados en Latin-1 o ISO-8859-1 (en español-castellano para entendernos).

Ahora surge la duda de cómo es que sí que nos deja el JXplorer hacerlo sin ningún tipo de problema. La explicación es que JXplorer así como otros muchos navegadores/administradores de LDAP, implementa el codificado automático del ISO correspondiente al idioma a codificación UTF-8, de modo que lo que está haciendo el cliente de administración de LDAP es codificar automáticamente a UTF-8, independientemente de que nosotros queramos o no.

Además de codificar en formato UTF-8, todas las entradas que contengan caracteres raros para LDAP (aquellos que no sean los caracteres estándar exployendo caracteres del tipo áéíóúäëïöüñç), deben además ir codificadas en Base64 (otro algoritmo de codificación), de modo que lo que son caracteres extraños para el LDAP que son los que genera UTF-8 (áéíóúäëïöüñç) cuando empleamos ISO 8859-1, deben ser transformados a Base64 de modo que JXplorer hace este tipo de transformación de Latin-1 (ISO-8859-1 -> UTF-8 -> Base64 por nosotros, y así, cuando exportamos una entrada que vemos que tiene acentos o eñes en el LDAP del MAP, vemos que las entradas son del tipo de un churro de caracteres raros... del tipo:

dn:: ZG46IGNuPUphdmllciBHw7NtZXogR2FyY8OtYSxvdT1wZW9wbGUsZGM9ZG9taW5p
 byxkYz1lcw==


que es el formato Base64 de un DN (los :: nos indican que está codificado en Base64) para la entrada:

dn: cn=Javier Gómez García,ou=people,dc=dominio,dc=es

que es a su vez el formato UTF-8 de un DN que corresponde con:

dn: cn=Javier Gómez García,ou=people,dc=dominio,dc=es

en formato ISO 8859-1 o Latin-1 (que tal y como comento es la ISO que identifica a nuestro idioma, el español).

Las codificaciones de Latin-1 a UTF-8 se pueden hacer o bien mirando unas tablas de UTF-8 o bien como hice yo con un sencillo javascript que traduce todo tipo de carácter raro para LDAP de modo que nos da la codificación sin mayor problema.


<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>

<script>
function encode_utf8(s){
return unescape(encodeURIComponent(s));
}

function decode_utf8(s){
return decodeURIComponent(escape(s));
}
</script>

</head>
<body>

<form name="formulario">
Texto a codificar en en formato<br />Latin-1 (ISO-8859-1) -> UTF-8<br />
<input type="text" name="entrada" value=""><br />
<input type="button" value="codificar"
onclick="document.formulario.resultado.value=(
encode_utf8(document.formulario.entrada.value));
document.formulario.salida.value=document.formulario.resultado.value"> <br /><br />

Texto a decodificar en formato<br />UTF-8 -> Latin-1 (ISO-8859-1)<br />
<input type="text" name="salida" value=""><br />
<input type="button" value="decodificar"
onclick="document.formulario.resultado.value=(
decode_utf8(document.formulario.salida.value));"><br /><br /><br />

Resultado<br />
<input type="text" name="resultado" value=""><br />

</form>

</body>
</html>


Una vez realizada la codificación a UTF-8 el mismo OpenSSL tiene una utilidad de línea de comandos que permite codificar y decodificar en Base64, con lo que quedaría el chorro de caracteres raros listos para introducir dentro del LDIF en el DN correspondiente.

Yo creo 3 archivos:

- plain.txt es el archivo con el texto plano en formato UTF-8
- coded.txt es el archivo con el texto UTF-8 codificado en Base64
- decoded.txt es el archivo con el texto en UTF-8 decodificado de Base64 (por motivo de comprobación más que nada)

de modo que tecleando desde consola

type plain.txt|openssl base64 -e > coded.txt

Codifica plain.txt en UTF-8 a coded.txt en Base64, y

type coded.txt|openssl base64 -d > decoded.txt

Codifica coded.txt en base64 a decoded.txt en UTF-8 comprobando que el plain.txt y el decoded.txt deben coincidir.

Probando de esta manera no hay problema en dar de alta desde línea de comandos preparando el archivo LDIF a las entradas que queramos asignar al nuevo árbol, siendo lo más engorroso quizás el tema de codificado con los diferentes algoritmos, pero que a fin de cuentas es la explicación que se tiene a por qué el JXplorer sí que daba de alta con acentos y eñes sin despeinarse...

De este modo tendríamos:

Latin-1 o ISO-8859-1 (1)


dn: cn=Begoña Gómez García,ou=people,dc=dominio,dc=es
objectClass: top
objectClass: usuario
cn: Begoña Gómez García
uid: bgomezg
userName: bgomezg


UTF-8 (2)


dn: cn=Begoña Gómez García,ou=people,dc=dominio,dc=es
objectClass: top
objectClass: usuario
cn: Begoña Gómez García
uid: bgomezg
userName: bgomezg


Base64 (3)


dn:: Y249QmVnb8OxYSBHw7NtZXogR2FyY8OtYSxvdT1wZW9wbGUsZGM9ZG9taW5pbyxk
 Yz1lcw==
objectClass: top
objectClass: usuario
cn:: QmVnb8OxYSBHw7NtZXogR2FyY8OtYQ==
uid: bgomezg
userName: bgomezg


Algunas notas a este tipo de codificaciones.

Cuando en el punto (2) tenemos el DN (dn: cn=Begoña Gómez García,ou=people,dc=dominio,dc=es), es el conjunto de DN el que tenemos que poner en el archivo plaint.txt para codificarlo en Base64, que si nos fijamos en (3), se codifica la entrada al completo (incluyendo los OU y DC de la entrada, pero NO dn:), de modo que estamos codificando realmente el texto:

cn=Begoña Gómez García,ou=people,dc=dominio,dc=es

Otro dato más es que cuando codificamos dicho DN completo a Base64, el texto plano que nos queda en el coded.txt es un texto del tipo:

Y249QmVnb8OxYSBHw7NtZXogR2FyY8OtYSxvdT1wZW9wbGUsZGM9ZG9taW5pbyxk
Yz1lcw==


Es decir, en 2 líneas, y tal y como sabemos (o deberíamos de saber), cuando tenemos 2 líneas y queremos introducirlas en un archivo LDIF, se comienza la segunda línea con un sólo espacio en blanco y a continuación el texto correspondiente a la segunda línea, de modo que esto indica al LDIF, que la línea con un sólo espacio en blanco es continuación de la línea anterior.

De modo que quedaría del modo:

Y249QmVnb8OxYSBHw7NtZXogR2FyY8OtYSxvdT1wZW9wbGUsZGM9ZG9taW5pbyxk
 Yz1lcw==


Importante recordar que la codificación en Base64 debe ir con :: para indicar este tipo de codificación al archivo LDIF.

Asimismo en la entrada correspondiente al CN de (2) (es decir, la línea cn: Begoña Gómez García), codificaríamos sólo en texto correspondiente al nombre de la persona (Begoña Gómez García) si el cn:, y del mismo modo podríamos los :: para indicar que es una entrada en Base64.

Confío en que más de uno solucione sus problemas con la codificación en ISO-8859-1 (Latin-1) para LDAP con esta pequeña entrada ;o)

technoratiquetas | | | |

6 comentarios:

Anónimo dijo...

que fumas?

Anónimo dijo...

Opino igual! que fumas ? :D

Es como muy complicado....

Anónimo dijo...
Este comentario ha sido eliminado por un administrador del blog.
publikaccion dijo...
Este comentario ha sido eliminado por el autor.
publikaccion dijo...

A mí me solucionó un problema que teneníamos con un LDAP de cerca de 100.000 usuarios, es complicado en la explicación, pero SABIENDO LEER y SABIENDO EN DONDE TE METES, se entiende perfectamente...

lo que Natura no da Salamanca no lo presta

rumagarcas dijo...

Un autentico crack