ADFS - Seguridad en Servicios Web

Seguramente si estás leyendo este artículo es porque ya implementaste seguridad con Adfs en tus sitios y ahora necesitás extender esa seguridad a los servicios web.

Sino te recomiendo que empieces por la serie dedicada a la autenticación y autorización en sitios web con Adfs.

Lo que vamos a hacer es permitir que la aplicación web indique al servicio web quien la está usando de una manera confiable y segura. A este proceso se lo llama Delegación de la Identidad.

Objetivos de la delegación

  • Controlar el acceso al servicio web tanto a nivel de la aplicación que lo utiliza como del usuario final.
  • Conocer, en el servicio web, quien es el usuario de la aplicación.

Escenario de delegación

El proceso de delegación ocurre en dos etapas, la primera es para acceder al sitio web donde el usuario presenta sus credenciales. La segunda etapa es para acceder al servicio web actuando como el usuario autenticado en el sitio. De aquí en adelante nos referiremos a “Juan” como el usuario y a “AppWeb” como las credenciales con que se autentica la aplicación web (ya sea indicando usuario y password o siendo la cuenta con que ejecuta el application pool del sitio).

ADFS - esquema de delegación

La negociación que incluyen los mensajes 1 y 2 es para obtener el token de ingreso a la “Aplicación Web”, a este token lo llamaremos “Token A”. Esta negociación en aplicaciones web se suele obtener en forma pasiva (a base de redirecciones del browser).

La negociación que incluye los mensajes 3 y 4 es la correspondiente al token para que AppWeb acceda a “Servicio Web” actuando como Juan, a este token lo llamaremos “Token B”. Esta negociación se realiza en forma activa, invocando alguno de los endpoints de los publicados en Adfs. En dicha invocación se incluye las credenciales de AppWeb y el “Token A”.

Pipeline de ejecución del Claim Engine

El siguiente gráfico es la representación de lo que sucede dentro de ADFS en el escenario de delegación. La mitad izquierda del gráfico corresponde a la negociación del “Token A” para acceder a la “Aplicación Web” mientras que la mitad derecha corresponde a la negociación del “Token B” para que la aplicación pueda acceder al “Servicio Web” actuando en nombre del usuario autenticado, es decir en nombre de Juan.

ADFS - flujo de claims con delegación

El pipeline de ejecución para obtener un token actuando como otro usuario (delegación) tiene diferencias con el pipeline de autenticación donde no se delega la identidad, algunas de estas son:

Existen dos conjuntos de claims de entrada, el correspondiente a AppWeb y el correspondiente a Juan. El primero proviene de la autenticación de AppWeb ya sea que AppWeb indica su password o utilizando kerberos o mediante un certificado. El segundo conjunto de claims proviene del “Token A” que identifica a Juan en la “Aplicación Web”.

La ejecución de reglas para la obtención del “Token B” alterna operaciones sobre los claims de AppWeb (quien delega) y los claims de Juan (en nombre de quien se actúa).

  1. Se ejecutan las “Delegation Authorization Rules” sobre los claims de AppWeb.
  2. Se ejecutan las “Issuance Authorization Rules” sobre los claims de AppWeb.
  3. Se ejecutan las “Issuance Authorization Rules” sobre los claims de Juan.
  4. Se ejecutan las “Issuance Transform Rules” sobre los claims de AppWeb.
  5. Se ejecutan las “Issuance Transform Rules” sobre los claims de Juan.

Si en alguno de los pasos 1, 2 o 3 no fuese autorizado el ingreso, se aborta la generación del “Token B” en ese paso.

Los claims de entrada para Juan en la obtención del “Token B” son los correspondientes a los claims del “Token A”, obtenido durante la autenticación de Juan a “Aplicación Web”.

Es necesario guardar el token inicial del usuario (Token A), esto se hace poniendo la propiedad saveBootstrapTokens en true (en microsoft.identityModel –> service).

Referencias

Implementando SOLID – LSP

Este es el tercer post de la serie Implementando SOLID, la cual surge de compartir con otros colegas las resoluciones que cada uno implementaría  al aplicar los principios SOLID. En esta ocasión vamos a implementar el principio de sustitución de Liskov pero antes de comenzar les comparto los links a otras resoluciones:

Resolución por Fernando: http://blog.kudewe.com/2012/08/implementando-solid-lsp.html

Liskov substitution principle:

El principio de sustitución de Liskov junto al principio abierta cerrado (OCP) son los que nos definen el camino para aplicar correctamente la herencia en el diseño orientado a objetos. Esto se debe a que, juntos, dan robustez al diseño y con robustez me refiero a la “capacidad para adaptarse a los cambios” en forma estable. Mientras que OCP hace que nuestro diseño pueda evolucionar de forma “segura” (principalmente utilizando herencia), LSP nos marca el camino de esa evolución para no abusar de dicha herencia.

Siguiente con los ejercicios utilizados en el evento, refactorizaremos el llamado LSP.MailBuilder de la siguiente forma:

  1. Ejecutamos los tests, todo verde, empezamos a refactorizar.
  2. Identificamos al método WithEntity de la clase ContactInformationMailBuilder como el objetivo de nuestra refactorización, según LSP no debería ser necesario conocer las clases derivadas de ContactInformation para funcionar, sino que debería funcionar con la clase base (en este caso ContactInformation) siendo sus derivadas las encargadas de extender el comportamiento.
  3. Pasamos el método ParseContactInformation a la clase ContactInformation.
  4. Ejecutamos los tests, todos verde.
  5. Sobrescribimos el método ParseContactInformation en la clase ContactInformationSubsidiary, agregando el comportamiento del método ParseContactInformationSubsidiary.
  6. Ejecutamos los tests, todos verde.
  7. Sobrescribimos el método ParseContactInformation en la clase ContactInformationAuction, agregando el comportamiento del método ParseContactInformationAuction.
  8. Ejecutamos los tests, todos verde.
Unable to display content. Adobe Flash is required.

En este momento, la familia de la clase ContactInformation aun tiene una pequeña responsabilidad en el parseo y es la de determinar la forma de relacionar el nombre de la propiedad con el valor, para solucionar esto hacemos un refactor mas: “Cambiar los parámetros de AddBodyLine a propertyName y propertyValue”.

Unable to display content. Adobe Flash is required.

Por último y para respetar el principio de responsabilidad única (SRP), podríamos hacer que el parser sea un objeto aparte y no una Action (callback), separando así la responsabilidad de parsear un ContactInformation de la de construir un Mail.

Unable to display content. Adobe Flash is required.

 

Otros artículos de la serie:

  • Principio de responsabilidad única
  • Principio abierto / cerrado

Implementando SOLID–OCP

Este es el segundo post de la serie Implementando SOLID, en esta ocasión vamos a implementar el principio Abierto (a la extensión) Cerrado (a la modificación).

Open close principle:

Los (micro)pasos seguidos para aplicar este principio se puede separar en 3 grupos de refactorización, el primer refactor tiene mucho que ver en el principio de responsabilidad única que vimos en el post anterior ya que estamos separando el criterio de filtrado de la aplicación de este criterio sobre la lista de productos. En el segundo refactor nos terminamos de preparar para aplicar el principio OCP (pasos 1 y 2) y lo aplicamos (paso 4). En el tercer refactor podemos ver como extendemos el funcionamiento sin modificar el código anterior.

Refactor 1: Separando responsabilidades de ProductoFilter.ByColor:
  1. Run tests, todo en verde, refactorizamos.
  2. En ProductFilter, extraemos el método Match a partir de la condición de filtrado.
  3. Run tests, todo en verde.
  4. Creamos la clase ColorCriteria, movemos el método Match a esta nueva clase y ajustamos el código.
  5. Run tests, todo en verde.
  6. Pasamos el productColor como parámetro en el constructor y ajustamos el código.
  7. Cambiamos el parámetro productColor del método ByColor por el criteria, cambiando de lugar la instanciación de ColorCriteria a la invocación de este método. Ajustamos el código.
  8. Run tests, todo en verde.


ver screencast

Refactor 2: generalizando ProductFilter.ByColor y ProductFilter.BySize
  1. Extraemos una interface de ColorCriteria, que llamaremos ICriteria y contendrá el método Match.
  2. Reemplazamos el type del parámetro criteria de ProductoFilter.ByColor a ICriteria y cambiamos el nombre del método a ByCriteria.
  3. Run tests, todo en verde.
  4. Creamos una nueva implementación de ICriteria para Size que llamaremos SizeCriteria, tomamos el código de ProductFilter.BySize, el cual borraremos y en su lugar invocaremos a ByCriteria. Ajustamos el código.
  5. Run tests, todo en verde.


ver screencast

Refactor 3: agregando implementación de ICriteria para filtrar por color y tamaño
  1. Creamos una nueva implementación de ICriteria llamada MultiCriteria que haga el Match contra todos los elementos de una lista de ICriteria.
  2. Borramos el método ByColorAndSize de ProductFilter y reemplazamos sus invocaciones por ByCriteria con MultiCriteria. Ajustamos el código.
  3. Run tests, todo en verde.


ver screencast

código fuente final: clic aquí

Y aquí les comparto la solución de Fernando para este mismo caso y la de Martín

Implementado SOLID – SRP

A fines de Junio asistí a un evento sobre principios de diseño SOLID, el mismo fue coordinado por Fernando Claverino y Carlos Peix y se desarrollo en las oficinas de Kleer.

A continuación vamos a intentar compartir implementaciones de estos principios, nos basaremos en los ejercicios utilizados durante el evento, focalizados en la resolución de un problema mas que en la explicación teórica del principio, sobre este último tema pueden consultar la VAN “Principios de diseño SOLID” dada por Carlos Peix.

Single responsability principle

Los pasos seguidos para aplicar este principio fueron:

  1. Probar que funcione.
  2. Identificar responsabilidades: lectura del archivo y manejo del formulario.
  3. Primera refactorización: identificar “secciones” de código y separarlas en métodos. Resultado: método AddItemToListView y ReadFile.
  4. Probar que siga funcionando.
  5. Segunda refactorización: identificar conjuntos de métodos y agruparlos en clases (aquí suelo tomar como criterio técnico los namespace que utilizan). Resultado: clase MyFileReader.
  6. Probar que siga funcionando.

Así terminamos teniendo una clase que es responsable del manejo del formulario y otra que es responsable de la lectura del archivo.

Unable to display content. Adobe Flash is required.

otras soluciones para este mismo principio:

Otros Principios:

Open / Close princpiple

Liskov substitucion principle

Interface segregation principle

Dependency inversion principle

Mvc4 y jquerymobile

En este post vamos a hablar sobre la integración de asp.net mvc v4 con jquery.mobile. Para quienes necesiten una breve introducción a jquery.mobile pueden leer el post sobre jquery.mobile

Cuando desarrollamos una aplicación que se tiene que ver tanto en un browser de PC como en un dispositivo móvil, la primera pregunta que debemos hacernos es: "¿es la misma aplicación o estamos hablando de dos aplicaciones diferentes?", y no me refiero a nivel técnico, sino a nivel funcional, muchas veces es diferente el uso que se le da a una aplicación desde un dispositivo móvil y desde una PC, en este post vamos a desarrollar la opción de que es la misma aplicación.

Vistas

Como sería de esperar, es en las vistas donde deberíamos tener las mayores diferencias para dispositivos móbiles y de escritorio.

Una facilidad que nos da mvc4 es la de "sobreescribir" páginas para ser utilizadas desde dispositivos móviles simplemente creando una página con el mismo nombre y sufijo .mobile, por ejemplo: Index.cshtml e Index.mobile.cshtml, la primera página se va a utilizar cuando accedamos desde una PC mientras que la segunda se utilizará cuando se acceda desde un dispositivo móvil. Lo mismo aplica tanto para partial views como para layouts.

Hay 3 casos de vistas (que a su vez aplican sobre view, partial view y layouts)
  1. Vistas que son totalmente agnósticas del dispositivo con que se accede. Es el caso de About.cshtml.
  2. Vistas muy diferentes en cuanto a estructura, por lo que amerita tener dos archivos diferentes. Es el caso de Films.cshtml y Films.mobile.cshtml.
  3. Vistas híbridas, donde las diferencias no causan problemas ya sea porque utilizan atributos que son ignorados por la otra modalidad y/o determinados class que, en el peor de los casos, no los encontrará. Es el caso de Disponibility.cshtml que solo agrega los atributos data-role y data-min que jquery.mobile interpretará.
Para este ejemplo las únicas vistas que son totalmente diferentes son: Layout, Film y DateTime. En el resto no hay diferencia estructural que lo ameriten.

Controllers

Los controllers son los mismos ya sea que se navegue de un dispositivo móvil o desde un desktop, quizás lo único interesante para mencionar en este post es el que nos permite cambiar intencionalmente entre vistas (mobile vs desktop):

Un caso particular se da cuando queremos redireccionar a otra acción con RedirectToAction, si bien la redirección parece funcionar a primera vista, cuando queremos accionar sobre la nueva página esta tiene como url la anterior provocando así un extraño comportamiento. La solución a esto es indicar manualmente el atributo data-url del div de la página. En el ejemplo que les comparto hubo un solo caso y está puesto directamente en el controller:

mientras que en _Layout.mobile.cshtml está:

Reconociendo dispositivos

Por default, la versión beta con la que estoy trabajando de mvc4, no reconoce muchos dispositivos mobile, como es el caso de mi Android, por este motivo es que hay que agregarlo.

Y en Global.asax.cs, debemos agregar al Application_Start la siguiente línea:


Algunas consideraciones

asp.net mvc4 y jquery.mobile son dos framework independientes y tienen algunas "fricciones", por ejemplo:
  • jquery.mobile maneja por default un esquema de navegación con request ajax y agregando nuevas páginas como div en función de los response obtenidos. Este funcionamiento tiene algunos roses con el RedirecToAction por ejemplo, la solución a este caso debe ir por el data-url y no por desactivar ajax ($.mobile.ajaxEnabled = false;).
  • Existen casos particulares donde no queremos utilizar la navegación default de jquery.mobile que utiliza ajax como explicábamos en el punto anterior, en estos casos tenemos dos alternativas rel="external" y data-ajax="false". Documentación sobre Navegación en jquery.mobile
  • Al utilizar jquery.mobile tenemos que considerar que tiene una estructura de eventos complementaria a jquery por el tema ya mencionado de la navegación. Por ejemplo debemos utilizar los eventos pageCreate o pageInit en lugar de ready. Documentación sobre Eventos en jquery.mobile

Ejemplo completo

Luego de descargar el ejemplo desde aquí y abrir la solución, es necesario ejecutar los siguiente comandos desde Packages Manager Console:

Los mismos son para instalar todas las librerías desde NuGet (asp.net mvc4, nunit, etc) utilizadas en el ejemplo la primera vez que compiles.

Otra opción es hacer clic derecho sobre la solución y tildar la opción "Enabled NuGet Package Restore" como dice el siguiente artículo: Using NuGet without committing packages to source control.

Empenzando con jquery mobile

Definitivamente las aplicaciones que hagamos en los próximos años van a tener que ser usables desde dispositivos móviles y para esto hoy contamos, entre otros, con jquerymobile de la familia de jquery para aplicaciones html.

Introducción breve

  • Para agregar comportamiento a los elementos html se utiliza (principalmente) el atributo data-role lo cual define, como es de esperar por su nombre, el rol que cumplirá el elemento.
  • Si bien se pueden organizar cada pantalla como un archivo html aparte, jquerymobile ofrece el concepto de páginas, ¿y como se indica este comportamiento?... si, si, con data-role="page".
  • También contamos con otros data-role como header, content, footer, dialog, listview, button, etc.
  • Un elemento importante de definir en el <head> del html es <meta name="viewport" content="width=device-width, initial-scale=1"> el cual determina que el ancho de la página es el ancho de la pantalla de dispositivo

Nota: al momento de escribir este post, la versión 1.1 está en rc1 (Unstable build) por lo que los ejemplos están desarrollados con la última versión estable (1.0.1).



Referenciando js y css

descargar

La estructura base de cualquier página (de este post) será:

Multipage

descargar ver imagen

Podemos definir varias page en un mismo html y para acceder a cada una de ellas lo haremos con # indicando así el fragmento (o recurso secundario) que queremos cargar:


Principales partes de una página

descargar ver imagen

Cada página se divide en 3 secciones principales: header, content y footer, el atributo data-position="fixed" indica que el elemento siempre se mantendrá visible.


Navigator

descargar ver imagen

Podemos agregar un menú para navegar entre las páginas, esto se hace con el data-role="navbar", armando una lista ul/li con elementos a en su interior como la siguiente:


Listas

descargar ver imagen
Una forma bastante elegante de armar una lista es con el data-role="listview" en un elemento ul:


Mejorando las listas

descargar ver imagen
Algunos efectos interesantes sobre las lista puede ser data-filter="true" que agrega un cuadro de filtrado, data-inset="true" mejora el aspecto y data-role="list-divider" para colocar elementos (li) como divisores:


Dialogs

descargar ver imagen
Al mismo nivel que page tenemos dialog, la principal diferencia entre ambos es que el primero ocupa toda la pantalla mientras que el segundo se muestra "por sobre" la pantalla anterior y tiene cierto comportamiento propio de un dialog (como el botón para cerrarlo).

para invocar al dialog, lo mismo que antes para una page, un elemento a con href="#message"



Hasta aquí llegó este post introductorio, les dejo un ejemplo completo que también pueden descargarlo (el cual contiene los css y js necesarios para ejecutarse).

Si les pareció un framework interesante, les recomiendo seguir por el sitio de jquery-mobile que no tiene desperdicio.

Primero pasos con Knockout

Luego de varios comentarios positivos sobre este framework y de ver que ya viene incluido en asp.net mvc4, decidí darle un vistazo a knockout 2.0, como resultado de estas pruebas podría decir que knockout me parece muy útil cuando queremos hacer una aplicación web con bastante comportamiento del lado del cliente (browser), es decir, cuando queremos que el armado de la vista (html) sea responsabilidad del cliente (browser) y no tanto del servidor.

¿Que aporta knockout la programación?

Si bien para tener vistas mas dinámicas me alcanzaba con jquery, el aporte del knockout está en no tener que manipular el DOM artesanalmente sino que de eso se encarga knockout en base a los data-bind que nosotros definimos.

¿Que conceptos se agregan respecto de la simple manipulación del DOM?

Se agrega el concepto de view-model observable, uno de los pilares de knockout (sinó "el" pilar).

Ejemplo completo

Descargar

Este ejemplo utiliza mongolab y funciona simplemente abriendo el html desde el filesystem con un browser, no necesita hosting del sitio web (a los fines didáctico).


Para configurar mongolab, siga los pasos descriptos en readme.txt

Conceptos claves

  • Separar correctamente lo que corresponde a la vista de lo que corresponde al view-model
  • Utilizar los data-bind para vicular atributos de html con propiedades del view-model o para iterar como el caso de data-bind="foreach: articles"
  • Todo lo que queremos que se actualice 'automáticamente' debe ser ko.observable(...), ya sean objetos, propiedades, arrays

Analizando algunos fragmentos de código

Para inicializar todos nuestros bindings, tenemos que ejecutar ko.applyBindings luego de que se cargó nuestra página:



Para relacionar un objeto de nuestro view-model con los elementos html debemos utilizar los data-bind, por ejemplo:

donde:

  • El data-bind del form indica que cuando se haga un submit de este form, se ejecute el método add de nuestro view-model y que los data-bind internos del form trabajan con el objeto newItem de nuestro view-model
  • El data-bind del input id="new_name" está indicando que la propiedad relacionada con el value de ese input es name. ¿de quien?, si, de newItem


y por último, veamos como iterar en un array de items:


  • en primer lugar podemos ver la propiedad length bindeada al contenido del elemento span.
  • luego, podemos observar un elemento ul cuyo data-bind nos dice que va a recorrer el array articles de nuestro view-model.
  • ahora bien, dentro de cada li vamos a tener 2 elementos de tipo span que van a estar relacionados con las propiedades name y stock de la misma manera que el span anterior pero con la salvedad que estos están vinculados a cada elemento del array que estamos iterando. También tendremos 2 elementos de tipo button y, como podemos intuir del data-bind cuando se presionen invocarán a los métodos set_current y remove de nuestro view-model (y no del elemento que estamos iterando, para eso es el $parent)


Y... ¿el view-model?

Ahora veremos algunos fragmentos que considero importantes del view-model, pero pueden verlo completo bajándose el ejemplo.


Aquí podemos observar como creamos el view-model (self) creando articles como un array observable y newItem como un objeto observable. Luego definimos length como una propiedad computable.

function articulos() {
    var self = { };
    self.articles = ko.observableArray();
    self.newItem = ko.observable();

    self.length = ko.computed(function() {
        return self.articles().length;
    });


Aquí podemos observar la definición de add, el parámetro f es el form involucrado, las primeras líneas son para validarlo (jquery.validation), luego tomamos el action definido en el form asumiendo que ese es el nombre de la colección a la que queremos agregar el nuevo elemento y llamamos a hacer un $.post con self.newItem() como contenido y, de resultar exitoso el POST, se agrega la respuesta (data) a la colección de articles, previo crearla como observable.

self.add = function(f) {
    var form = $(f);

    var valid = true;
    if (form.valid !== undefined)
        valid = form.valid();

    if (valid === undefined || valid) {
        var collectionName = form.attr("action");

        rest.dopost(collectionName, self.newItem(), function(data) {
            self.articles.push(observableArticulo(data));
            self.reset_newItem();
            self.sort();
        });
    }
    return false;
};


Descargar ejemplo completo