Thursday, July 4, 2013

WebUI component lifecycle

Once again, I'm drafting a not-too-trivial web app with Dart. I have to say that the language really does feel nice, and while it's getting better each week, it's not so unstable to have to be learned again on each upgrade.

One major attraction of Dart is the WebUI library, which is still far from complete, but changes the whole app design so much that any other option feels hacky by comparison. Unfortunately, there's still many rough edges, and while the documentation is very clear and and readable, there's still some things that aren't well laid out, specially about the intended architecture.

So, these are the things I'm struggling with right now.  I guess i'll turn some of these into questions to post on the +Dart G+ page, but first I want to spill my doubts here:

1.- Create components 'on the fly'.
All the WebUI examples are about instantiating components simply by using the tags in some HTML. Behind the scene, the compiler generates some tedious-looking Dart code that does everything; but there doesn't seem to be an easy way to just create a new component from Dart code.  After finding some hints reading between the lines of some docs, and distilling from the generated code, I came up with something like this:

  /// Adds a component to the host.
  ///     [holder] is the container Element,
  ///     [comp] is the already-created component object,
  ///     [compname] is the tagname of the component.
  void insertComponent(Element holder, WebComponent comp, String compname) {
    comp.host = new Element.html('<${compname}></${compname}>');
    _lifecycleCaller = new ComponentItem(comp)..create();
    holder.children.add(comp.host);
    _lifecycleCaller.insert();
  }


To create the component, the calling code must first create an instance of the Dart object and call this function, with the correct tagname.  Which leads to:

2.- Why can't the component class publish it's tagname?
It would be as simple as adding a final tagname = "x-tag-name"; to the autogenerated class declaration. In fact, the tagname is already inserted as a constant several times; but only for internal use, never to return to the caller.

3.- Does every component need a ComponentItem nanny?
Also, do you see the _lifecycleCaller object created to insert the component? Well, this code in fact isn't a free-standing function, it's a method of a class I use to handle "component container" (or "holder" as called on the code).  There, I also have to store this caller and use it after removing the DOM element.  I guess it's fair, given the name: it's a life cycle method caller, so it has to stay around for the whole life cycle.  But it would be nicer if the three methods it implements were simply part of the WebComponent class, so i could just call comp.create();, comp.insert(); and comp.remove(); at the appropriate moments.  Even better if both .insert() and .remove() could be called automatically at the DOM insertion/removal.

In short, this is how I'd like to create a component and insert it to a holder element:

  var comp = new MyComponent(...);
  comp.create();
  var holder = query('#componentholder');
  holder.children.add(comp.host);

or even:

  var comp = new MyComponent(...);
  query('#componentholder').children.add(comp..create()..host);

which is even more readable.

While writing this, I get more and more the feeling that I'm getting needlessly complicated.  It doesn't seem to be an easier way already implemented (in part because the generated code does similar things), but I can't fight the feeling that maybe I don't need any of this.

Saturday, April 7, 2012

SSH para dummies decididos

Recientemente, aconsejé a un amigo que use ssh para manejar un server; me sorprendió el número de problemas que tuvo, a pesar de tener larga experiencia con todo tipo de sistemas, incluso varios tipos de programación desde hace muchos años.

¿Que paso? No es que mi amigo estuviera poco preparado, ni tampoco que le haya sugerido algo muy difícil o complicado.  No, yo creo que el problema está en que nunca había tenido necesidad de usarlo, porque ha podido usar varias otras soluciones en cada caso que se le ha presentado.

Entonces, ¿por que mi insistencia en que use ssh, en lugar de esas otras opciones que ya conoce? Pues resulta que no sólo ssh es una única herramienta que hace muchas cosas, ayudando en muchos problemas diferentes; sino que en la mayoría de los casos es, por mucho, la mejor solución disponible.

Como tantas otras cosas, una vez dominado abre un sinnúmero de posibilidades que antes uno no habría imaginado que eran posibles, ni que estaba a sólo un paso de poder resolver problemas aparentemente difíciles con tanta facilidad que a veces ni vale la pena grabar la solución para usarla mas tarde. ¡Es igualmente fácil volver a resolverla desde cero!

Muy bien, bastante propaganda.  ¿Que hace el dichoso ssh?  En principio es simple: crea una conexión encriptada entre un cliente y un server, y sobre ella puede abrir un terminal o un túnel para otros programas. también incluye utilitarios para copiar files.  Para mantener la mayor seguridad, incluye un muy buen sistema de identificación y autentificación.  Y finalmente, es usado por varios otros programas o sistemas para facilitar toda clase de conexiones seguras y transferencias de datos.

Nota: aunque 'ssh' es el nombre original de un programa que ahora es comercial, existe también una versión Open Source cuyo nombre real es OpenSSH.  Personalmente, sólo he usado la versión Open Source y es de ella que hablo en esta nota; pero al igual que todo el mundo, la llamo simplemente 'ssh'.

Teoría:

Todo se basa en un esquema llamado Criptografía Asimétrica (o Criptografía de Clave Pública, o Public-key Cryptography).  En este esquema, cada individuo posee dos claves relacionadas: una clave pública y una clave privada.  Ambas son generadas como un par, y poseen varias propiedades matemáticas que las hacen muy útiles.

La clave pública se puede distribuir abiertamente, con la intención de que todo el mundo sepa a quién pertenece. La clave privada, en cambio debe permanecer en estricto secreto.  Si por algún motivo hay posibilidades de que una clave privada haya sido copiada, es importante revocar la contraparte pública y generar un nuevo par.

En principio, cuando se una una de estas claves para codificar un mensaje cualquiera, sólo se puede decodificar usando la otra clave. En esto se diferencia de la criptografía simétrica, o de una sola clave. De este modo, es fácil enviar un mensaje que sólo lo pueda leer una persona: basta con usar la clave pública del destinatario y sólo esa persona será capaz de decodificarlo.

Del mismo modo, si una persona codifica con su clave privada un mensaje, cualquiera puede decodificarlo usando la clave pública.  Esto sería útil para asegurar quién es el autor del mensaje.  Por comodidad, no suele codificarse el mensaje completo, sino sólo un checksum o hash generado con el contenido del mensaje, formando una "firma digital" que puede agregarse al mensaje para asegurar no sólo la autoría del mensaje, sino que no ha sido alterado desde que fue firmado.

Finalmente, dos personas pueden establecer un canal privado de comunicación usando un par de claves para cada uno. El "intercambio de claves Diffie-Hellman" es un algoritmo que cada uno de los dos involucrados realiza usando su propia clave privada y la clave pública del otro para calcular una tercera clave, que es la misma para ambos, sin necesidad de transmitirla en ningún momento.  Luego pueden usar esta clave común para aplicar una codificación simétrica a cualquier mensaje que deseen intercambiar.

Aplicación:

Tanto ssh como SSL usan la criptografía asimétrica, y principalmente el intercambio Diffie-Hellman para asegurar privacidad, seguridad y autenticidad en la transmisión de datos. Las claves utilizadas para el protocolo SSL se manejan en los llamados 'certificados', y existe una extensa infraestructura de entidades firmantes y procesos establecidos para distribuir dichos certificados.  Esto lo hace altamente práctico para encapsular otros protocolos de forma automática; pero esto implica una serie de requisitos que pueden ser muy confusos en su aplicación.

En cambio, ssh es una aplicación independiente, con su propio juego de claves en la que uno mismo es responsable de crear el par, distribuir la clave pública y proteger la privada, así como mantener la relación de identidad con las claves.

Uso básico:

El uso básico del ssh como cliente es simple:
    ssh [username@]hostname
Este comando intenta abrir una conexión con el server hostname, puerto TCP 22, intercambia varias claves, a veces hace algunas preguntas al usuario, intenta establecer la identidad de cada uno, quizás pregunte un pasword; y si todo va bien, abre un shell para ejecutar comandos remotamente de forma interactiva.
Si no se especifica username (separado del hostname con una '@') intenta usar el mismo nombre de usuario que en el cliente.

Identificación del server:

La primera vez que un cliente se conecta con un server determinado, el ssh presenta al usuario con una serie de números hexadecimales y la pregunta:
    The authenticity of host 'shell.example.com (x.x.x.x)' can't be established.
    ECDSA key fingerprint is f7:ae:3a:90:de:f6:54:90:df:f2:e2:82:fc:62:64:d4.
    Are you sure you want to continue connecting (yes/no)?
Intimidante, ¿no? Es la primera vez, y empieza advirtiendo que no sabe si el server es el correcto.

Si uno lo piensa con cuidado, tiene sentido.  Si es la primera vez, no tiene forma de saber si estamos conectando con quien queremos conectarnos.  Usualmente no hay motivo para dudarlo, de modo que basta con responder 'yes'.

En casos de alta seguridad, o si hay motivos para creer que la red se encuentra comprometida, el responsable del server puede buscar alguna forma de enviar de antemano el 'fingerprint' del server para que el usuario lo compare con esa serie de hexadecimales.

En cualquier caso, el cliente ssh registra la clave pública del server, y la próxima vez que se conecte con el mismo server, no debe aparecer esa advertencia.  A menos que ocurra algún cambio en el server y en la nueva conexión utilice una clave pública diferente.  En ese caso, el cliente muestra una advertencia mucho más severa, y se rehúsa a conectar con ese server hasta que se resuelva la discrepancia.

Supuestamente, esto sólo debería ocurrir si ha ocurrido alguna interferencia con la comunicación y el server que nos está respondiendo no es el mismo que hemos contactado antes en la misma dirección.  Sin embargo, también ocurre lo mismo cuando el server ha sido reinstalado y se regeneró el par de claves.  En ese caso, el usuario debe borrar el registro de la clave anterior para hacer nuevamente el proceso de la primera conexión.  Para eso basta con borrar una línea del file ~/.ssh/known_hosts.  La línea exacta está indicada en el mensaje de advertencia.

Identificación del cliente:

Antes que el server permita al cliente ejecutar cualquier proceso, es necesario que acredite su identificación. Para esto existen varios métodos, los dos mas comunes son un pasword convencional, y una firma criptográfica.

El método mas conocido es el pasword convencional. Es también el menos seguro, por lo que sólo se usa cuando el cliente o el server han agotado todas las otras opciones.  Consiste simplemente en que el usuario conozca el pasword necesario para el username indicado en el server.  Por supuesto, este pasword es transmitido sólo después de haber establecido un canal seguro (usando claves criptográficas temporales, generadas en el momento de la conexión).

El segundo método es mucho más seguro y sólo resulta confuso las primeras veces. El primer paso es asegurar que el cliente tenga al menos un par estable de claves pública y privada. Luego hay que copiar la clave pública al server, de modo que cuando el cliente intente conectarse, pueda usar la contraparte privada para asegurar su identidad.

Las claves usadas por ssh para identificación suelen tener los nombres ~/.ssh/id_rsa o ~/.ssh/id_dsa, cada una con dos files, uno para la clave privada y otro con el mismo nombre mas la terminación .pub para la clave pública.

Cuando el cliente se conecta con el server, transmite un nombre de la forma username@clienthost que indica la identidad del usuario que intenta probar.  El server, entoncer verifica en el file ~/.ssh/authorized_keys si existe una línea con la clave pública para tal identidad. Si lo encuentra, se lo indica al cliente, junto con un bloque de datos aleatorio.  El cliente entonces usa su clave privada para codificar esos datos y los retransmite.  Si el server entonces es capaz de decodificarlos usando la clave pública, la identidad ha sido demostrada y se abre la conexión sin necesidad de ingresar un pasword.

ssh-keygen:

Es el programa usado para generar pares de claves.  En muchos casos, durante la instalación del paquete ssh, ya se ha generado un par de claves básico, que puede ser usado sin problemas.

De no ser así, o si es necesario un nuevo par, se usa el ssh-keygen; que permite una ampla variedad de opciones.  Las dos principales opciones son el tipo de clave, y un posible pasword.

Existen varios tipos de claves, y el ssh puede usar indistintamente varios de ellos, por lo que muchas veces no importa realmente cuál usar.  Los mas usados son RSA y DSA.  Siendo posiblemente mas común el RSA.

El pasword es una decisión personal.  En este caso, no se aplica al cliente, ni al server, ni a la conexión entre uno y otro, sino al par mismo.  Mas exactamente, a la clave privada.  La idea es: para reducir las probabilidades de que una clave privada sea robada, no se almacena directamente en el file ~/.ssh/id_rsa, sino que se encuentra codificada mediante el pasword.  Solo conociendo el pasword es posible desencriptar la clave privada para poder usarla.

Estrictamente hablando, no es necesario ponerle una clave a la clave.  De este modo, es posible abrir conexiones ssh sin que se haga ninguna pregunta al usuario, lo que puede ser importante para ejecutar comandos en un script.

Continuará...

Hasta ahora hemos visto la base teórica y cómo se conecta el cliente con el server.  En la próxima, hablaré de los diferentes usos del ssh: un shell remoto, transferencia de files, túneles, etc.

Wednesday, February 23, 2011

Palabras que no existen

Por favor, dejen de usar estas palabras:

  • "accesar": Mala traducción del inglés access. La palabra es acceder.
  • "aperturar": Pseudoderivación de apertura. La palabra es abrir.
  • "recepcionar": otra pseudoderivación, esta vez de recepción. La palabra es recibir.
También está el problema de palabras que se usan en lugar de otras:
  • "visualizar" no es una forma 'elegante' de decir "ver". No es correcto decir "Abro el programa; pero no puedo visualizar mi data", tampoco "entonces el sistema te visualiza las opciones". Visualizar en realidad significa "crear una visualización", que es una representación visual de un concepto no visual. Por ejemplo, crear un gráfico estadístico. Ojo, no es el acto de mostrarlo, sino de crearlo.
  • "descargar" es una mala traducción del inglés download, que no significa 'retirar una carga' (eso sería unload), sino 'mover una carga a un nivel mas bajo', en contraparte a upload. Un término más apropiado es "bajar".
  • "Fuente" por "tipografía". En inglés, el término font se ha usado desde la invención del tipo móvil y viene del francés fondue y fonte, que significa 'algo que ha sido fundido', por el proceso de fundición de tipos metálicos para la prensa. No tiene nada que ver con el uso de font por algunos tipo de fuente de agua, como derivación de 'fountain'.
Estas son las que se me ocurren de momento; seguro voy a ir agregando mas...

Friday, January 21, 2011

"extends" or "extended" ??

I've just read a post on the Django-users mailing list. It was a simple mistake, and quickly corrected by other participants of the list; but it made me think a bit about semantics...


The point was about Django template inheritance: as most OOP languages, Django lets you define a 'base' template and then another 'derived' template that 'extends' the base one. The user in question then used the base template and expected it to contain the 'derived' parts. As everybody pointed, he should have called the 'derived' template instead. Again, just like on OOP languages, where you instantiate the derived classes, and they inherit behavior from its base class(es).

But this kind of mistake seems to be a common one among those first approaching template inheritance. Why is this so? My guess: because 'extends' is a transitive verb.

Lets say I have a chair, and everybody around here knows what a chair is and how it looks. Then I come with some tools and "extend the chair" maybe add some armrests. What do I have now? the same chair, with armrests. If somebody else asks for "the chair", he'll get the 'extended' chair.

But that's not how OOP inheritance works: a class that 'extends' another doesn't make any change to the base one. It's a new class with inherited behavior. That's old news to any programmer.

Don't you think that the 'extends' keyword in Django templates and Java class declarations is a little misleading? I understand that it means "I'm just like that, but extended"; but how come it uses 'extends'? To me, it sounds like some pompous marketing: "this newfangled gizmo copies and extends the old system"