lunes, 29 de septiembre de 2008

Encriptacion de entradas en el Web.Config

Algo que me resulto realmente muy útil y muy sencillo de utilizar fue la herramienta que trae el SDK de .Net para poder encriptar distintas entradas o claves alojadas en el archivo Web.Config Con anterioridad a esta utilidad, una de las cosas que tocaban hacer era en el momento de hacer el deploy de una aplicación, era crear alguna utilidad mediante la cual el usuario, por lo general alguna persona del área de operación o infraestructura, pudiera editar claves o valores de configuración, encriptarlas o desencriptarlas para poder cambiar sus valores. Paso comentar alguna de las características de esta herramienta, aspnet_regiis.exe. Para comenzar las posibilidades de encriptacion son: mediante algoritmo RSA (Trafico de claves publicas y privadas) o mediante DPAPI (Data Protection application programming interface ). No es necesario tener el entorno de desarrollo instalado en la maquina en la que se quiere realizar la encriptacion/desencriptacion, si tiene que estar instalado el .Net Framework.

Me resulto bastante más útil el hecho de utilizar RSA por el hecho de que por lo general hay aplicaciones que necesitan ser desplegadas en un conjunto de servidores y con RSA se tiene la posibilidad de exportar la clave para después ser llevada a cada server en el que necesitemos desplegar nuestro sitio. Yendo al punto en cuestión, la herramienta puede ser accedida por línea de comandos como se accede a cualquier otra herramienta del SDK de .Net, desde el menú de Inicio->All Programs->Microsft Visual Studio 2005->Visual Studio Tools->Command Prompt Aquí las dos primeras opciones: El comando para encriptar puede trabajar tanto como rutas relativas o "virtuales” como absolutas o a la hora de indicarla la ubicación de nuestro web.config por lo que si queremos trabajar con rutas absolutas es aconsejable ubicarse en el mismo directorio físico en el que se encuentra el archivo a encriptar.

Un vistazo de mi web.config antes de ser encriptado seria algo asi:




Ahora si:
Si vamos utilizar parametros pasando la ruta virtual la opcion seria:

aspnet_regiis -pe "SeccionAEncriptar" -app "RutaVirtual"

Si vamos utilizar parametros pasando la ruta fisica de la aplicacion la opcion seria:

aspnet_regiis -pef "SeccionAEncriptar" -app "RutaFisica"

Por defecto, es decir si no especificamos un proveedor de encriptacion o metodo se realizara con RSAProtectedConfigurationProvider, podemos sin embargo combiarlo si lo especificamos en el parametro -prov y quedaria de la siguiente manera


aspnet_regiis -pef "SeccionAEncriptar" -app "RutaFisica"–prov "DataProtectionConfigurationProvider"


Para realizar la desencripatcion de los datos los procedimientos son los mismos, con la diferencia de que el parámetro pasado a al ejecutable aspnet_regiis deber ser: -pdf (en lugar de -pef si es que usamos rutas absolutas o fisicas ).

En este punto estamos ya en condiciones de tomar algunos recaudos:
La encriptación/desencriptación deberá realizarse en el mismo entorno en donde se encontrara instalada la solución, es decir una vez encriptados los datos, el archivo web.config no podrá ser desencriptado en otra máquina, esto es debido a que cuando utilizamos este comando las claves privadas se almacenan localmente en: C:\Documents and Settings\All Users\Datos de programa\Microsoft\Crypto\RSA\MachineKeys, demas esta decir que debemos tener permiso de escritura sobre esta carpeta.

Esta ultima aclaración servirá para darnos cuenta que al importarse la clave a nivel local en nuestra maquina, es muy probable que en un entorno distribuido (granja de servidores o server farms) algo pueda estar fallando, efectivamente deberemos asegurarnos de que nuestro web.config cuenta con la sección en la cual podremos indicar cual será nuestro contenedor de claves, la especificación de esta sección (configProtectedData) debería ser algo como:




Restaría entonces crear el contenedor de claves de la siguiente manera:

aspnet_regiis -pc "MiContenedor" –exp

Se crea así un nuevo contenedor para la clave de encriptación RSA, asignando el nombre "MiContenedor" y por ultimo:

aspnet_regiis -pa "MiContenedor" "ASPNET"

Donde "MiContenedor" deberá ser el mismo nombre asignado en –pc del comando anterior y que se corresponde con keyContainerName="MiContenedor"/> de webconfig.

Una ultima acotación sobre este tema que me pareció realmente practico, todo esto es a nivel de configuración de nuestro sitio, es decir que el acceso a las entradas de nuestro web.config desde el código sigue siendo igual que si no hubiéramos tocado nada, si lo que encriptamos fueron nuestras cadenas de conexión el acceso seria:

string strConString = ConfigurationManager.ConnectionStrings["MiConectionString"].ConnectionString;



Eso es todo y así de simple :-)

Ariel Serlin

viernes, 5 de septiembre de 2008

Workflow State Machine y Persistence Service

En la primera entrada de este blog voy a tratar de mostrar un ejemplo sencillo de utilización de maquinas de estado creadas con Windows Workflow Foundation. Como un amigo me consulto hace poco como haría para trabajar con State Machine Workflows y además utilizar el Persistence Service (serializar un workflow en una base de datos temporalmente) aprovecho la volada para hacer el post.

La idea básicamente de este tipo de workflow es modelar los distintos estados por los que podría pasar un documento o ítem cualquiera como por ejemplo, una factura, un comprobante o cualquier otra cosa que pudiera necesitar ser creado, aprobado, revisado y pueda volver a cualquier de estos estados anteriores.

El primer paso para comenzar a trabajar es ver que tengamos en nuestra PC todos los requisitos para poder trabajar con Workflow Fondation.

En caso de Visual Studio 2005, deberemos tener instalado la versión 3.0 del .Net Framework, las extensiones para workflow foundation y haber creado las bases de datos de persistencia de y tracking de Workflow Foundation, los scripts de creación están generalmente en el directorio: ( C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\EN )

Verificados estos requisitos podemos abrir Visual Studio y comenzar a trabajar.

El primer paso será crear un nuevo proyecto del tipo State Machine Workflow Librery



Una vez creado el proyecto, podremos comenzar a trabajar con el diseñador de workflow haciendo doble click sobre la clase creada por defecto "Workflow1.cs", en este ejemplo asumiré que solo se trabaja con dos estados posibles, más un estado final.
El diseño de lo que puede suceder en cada estado que identifiquemos seguramente podrá variar mucho de acuerdo al problema que se esté modelando, pero básicamente la idea que trato de mostrar en este caso sería como pasar de un estado a otro, ejecutar algo de código al pasar a ese estado y luego suspender o serializar el workflow hasta que necesitemos utilizar esa instancia nuevamente.

Para hacer que nuestros estados "escuchen o esperen" realizar alguna acción deberemos prepararlos o hacer que se suscriban a un determinado evento, esto seria básicamente agregando tantos Shapes de EventDriven como eventos queremos que se manejen en un estado.



Una vez realizado esto el paso siguiente seria manejar cada uno de los eventos que queremos capturar en cada estado y por cada EventDriven que hayamos manejado. Es aquí donde viene la parte en la que tenemos que escribir un poco de código...,

Necesitaremos primero una clase que herede de ExternalDataEventArgs y de esta forma poder dar a nuestra clase la capacidad de enviar datos a cuando se lanzan eventos, es muy importante que esta clase está marcada con el atributo [Serializable].

El paso posterior a este, seria modelar una interfaz de comunicación la cual deberá estar marcada con el atributo [ExternalDataExchange] y declarar en esta interfaz todos los eventos que vallamos a lanzar en una clase que implementará la interfaz en cuestión, adelantando un poco de que se trata esto el sentido real de esta interfaz de comunicación es poder decir a nuestros estados cuales son los métodos que deberán estar escuchando una vez instanciado nuestro workflow.





Siguiendo con la misma línea tocaría ahora construir una clase que implemente la interfaz recién modelada. De esta forma estaríamos ya en condiciones con continuar el modelado de nuestro flujo de trabajo y teniendo ya las clases que exponen los métodos declarados en la misma podemos avisarle a nuestro flujo de trabajo en qué momento deberá escuchar algún evento o serializarse y persistirse en una base de datos. Para poder hacer esto deberemos hacer doble click sobre alguno de nuestros EventDriven, como comentaba al principio dentro de cada de EventDriven podemos hacer muchas cosas pero a fines explicativos solo pasare de un estado a otro o simplemente persistir el flujo de datos en la DB de persistencia de WorkflowFoundation, para cualquier de los dos casos haciendo doble click sobre cualquier EventDriven podemos agregar Shapes de HandleExternalEvent, que como su nombre lo indica mediante estos podríamos manejar los eventos que fueron lanzados e identificados por los EventDriven, por supuesto después de la ejecución cada EventDriven podemos hacer invocar un Suspend (mas abajo explico un poco mas ) o un SetState
Con los segundo SetState principalmente logramos que nuestro workflow pase de un estado a otro, sencillamente lo que debemos hacer al usar estos shapes es decirle a que estado deberá moverse el workflow cuando la ejecución paso por el mismo, algo muy bueno que vi en este tipo de workflows es que a medida que vamos crenado las distintas transiciones mediantes nuestros SetState dentro de nuestros posibles estados, workflow se encarga de mostrar en forma grafica las nuevas transiciones




Respecto de los Suspend, es muy importante verificar la configuración de los dos parámetros TimeSpan que pide el servicio de persistencia de Workflow Foundation (Persistence Service) al momento de agregar este servicio a nuestro workflow, en el caso del primer parámetro indica durante cuando tiempo nuestro workflow será "dueño" de esa instancia, y el segundo le indica a WF cada cuánto va a ir a nuestra DB a buscar workflow que hayan expirado, teniendo las precauciones recién mencionadas básicamente dotamos a nuestro workflow de la capacidad de automatizar totalmente la persistencia de nuestras instancias en una DB (es válido aclarar que tiene que ser SQL2005).



Finalmente mi workflow tiene una forma como la siguiente:


Para terminar algunas acotaciones de cosas que me parecieron realmente muy buenas y otras no tanto.
Una de las buenas es la rapidez con la que se puede modelar un diagrama de estados, incluso esta bueno sentarse con el usuario al lado y mostrarle como quedaría la lógica de su proceso, pocas veces he tenido la oportunidad de hacer que el usuario me diga "en tiempo de diseño", no esto debería ser de la siguiente manera....
Otra muy buena es que Workflow Fondation provee un conjunto de clases que mediante Generics nos indican el estado actual y los próximos estados posibles de nuestras instancias y la búsqueda de toda esta info viene gratis con el servicio de persistencia. Pueden ver esto implementado en el método [WorkflowStates verEstados(Guid pguid)] de la clase
La no muy buena es la especificación de las excepciones, me parecen poco claras cuando ocurren, pero seguro que en algún momento la van a mejorar


Adjunto el código correspondiente al proyecto de workflow y una pequeña aplicación de prueba, espero que sirva


Ariel Serlin

Para bajar el codigo hace click aqui