WIF es sencillo, úsalo #3-1

En las anteriores entregas #1 y #2 empezamos por exponer las motivaciones de esta nueva tecnología salida de la factoria de Microsoft así como de conceptos fundamentales que deberemos entender, tanto concrétamente de WIF como conceptos generales que aplican a otras tecnologías de seguridad. A lo largo de esta tercera entrega, y de las posteriores que seguiran a esta, intentaremos mostrar pequeños ejemplos de uso que nos permitan ver como integrar WIF dentro de nuestras aplicaciones no tiene porque ser traumático y como realmente ganaremos muchas ventajas con respecto a sistemas  más tradicionales.

Cliente WCF Activo

En este primer ejemplo intentaremos mostrar como incluir un STS dentro de un servicio expuesto por Windows Communication Foundation y como hacer uso de el. Posteriormente interemos modificar configuración/código para obtener nuevas funcionalidades o cambiar la parametrización de funcionamiento.

 

1º Paso – Definicion del servicio

El primer paso a realizar será el de disponer de un servicio de WCF, en nuestro caso, usaremos un sencillo ejemplo de un servicio definido tal y como sigue en los siguientes fragmentos de código. Este servicio, por comodidad se ha expuesto en una aplicación de consola, pero lo mismo aplica si fuera un servicio de windows o bien un sitio web.

 

    [ServiceContract(Name="ICrmManager",Namespace="http://www.plainconcepts.com/wif/samples")]
    public interface ICRMManager
    {
        [OperationContract()]
        void Add(Customer customer);

        [OperationContract()]
        IList<Customer> Find();
    }

2º Utilizando Federation Utility

Una vez que tenemos nuestro contrato implementado y el servicio configurado (todo lo relativo a hosting y configuracion de WCF es algo que daremos por sentado, sino estos posts se alargarían demasiado), podemos hacer uso de las herramientas de Windows Identity Foundation de una forma muy sencilla. En el menu contextual de nuestro proyecto, veremos una entrada con un texto similar a “Add STS Reference”, esta entrada del menu contextual nos permitirá lanzar la herramienta “Federation Utility” ( incluida con el SDK de WIF) gracias a la cual podremos configurar el servicio para trabajar con un nuevo STS o bien un STS existente.

 

 

 

1

 

2

 

 

 

Este asistente nos pedirá los datos relativos al path del archivo de configuración de nuestro servicio wcf ( un web.config si es un sitio web ) y la URI del mismo. Esta uri es importante puesto que la misma servirá como valor de AudienceUri, es decir, como indicador de “para quien será el token generado por el STS”. Una vez configurado estos elementos el asistente nos preguntará si quieremos trabajar con un nuevo STS o un STS existente. Para  nuestro caso, seleccionaremos la opción crear un nuevo STS, tal y como se puede ver na la siguiente figura ( en el caso de disponer ya de un STS podemos establecer la direccion de su metadata).

 

4

 

3º Revisión de la configuración

 

El trabajo del asistente anteriormente ejecutado se traduce, como se puede ver en la ultima pantalla de resumen, en una serie de cambios a la configuración de nuestro servicio WCF, asi como a la inclusión de una serie de certificados a nivel de máquina ( estos certificados están pensados para el entorno de desarrollo y como veremos más adelante se usarán internamente por el STS para el cifrado y la encriptación de los tokens).

En cuanto a los cambios en la configuración, podemos ver la inclusión de un nuevo endpoint incluyendo un binding compatible con la federación de credenciales:

   <endpoint address="" 
                   binding="ws2007FederationHttpBinding" 
                   contract="CRMService.ICRMManager" 
                   bindingConfiguration="CRMService.ICRMManager_ws2007FederationHttpBinding" />

A mayores del endpoint, dentro de la configuración también se incluye un comportamiento nuevo ( no existente por defecto en WCF pero incluido con una extensión ) llamada FederatedServiceHostConfiguration.

<federatedServiceHostConfiguration name="CRMService.CRMManager" />

Este comportamiento tiene como objetivo permitirnos especificar la configuración de WIF que vamos a utilizar. Esta configuración, se establece con una nueva sección personalizada por WIF llamada microsoft.identitymodel.

  <microsoft.identityModel>
    <service name="CRMService.CRMManager">
      <audienceUris>
        <add value="http://localhost/CRMService" />
      </audienceUris>
      <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <trustedIssuers>
          <add thumbprint="6C6CE1DA53CE9F34F9DB4FFFAADDA8D84468C6BF" name="http://localhost:2325/CRMService_STS/Service.svc" />
        </trustedIssuers>
      </issuerNameRegistry>
    </service>
  </microsoft.identityModel>

4º Revisión del STS creado

6

Ahora, en nuestra solución, dispondremos de un nuevo proyecto, por defecto generado como un sitio web, que contiene el código de un STS personalizado, cuyo código está contenido dentro de la carpeta especial de ASP.NET App_Code. Este código, contiene un helper para trabajar con los certificados, por defecto con los generados con la herramienta aunque los mismos pueden ser modificados cambiando en las appsettings de nuestro sitio web las claves:

 <appSettings>
    <add key="IssuerName" value="ActiveSTS"/>
    <add key="SigningCertificateName" value="CN=STSTestCert"/>
    <add key="EncryptingCertificateName" value="CN=DefaultApplicationCertificate"/>
  </appSettings>

Junto con este helper, también tenemos la definición de nuestro SecurityTokenService personalizado, en forma de la clase:

 

public class CustomSecurityTokenService
     : SecurityTokenService
{
}

Este STS personalizado dispone de un método especial GetOutputClaimsIdentity cuyo objetivo es la de ofrecer el conjunto de Claims que hay que otorgar al usuario que se ha autenticado en el STS, el código por defecto de este método, que podremos ver en el siguiente fragmento de código, nos ofrece, para cualquier usuario, dos claims, una claim de tipo nombre y una claim de tipo rol.

 

    /// <summary>
    /// This method returns the claims to be issued in the token.
    /// </summary>
    /// <param name="principal">The caller's principal.</param>
    /// <param name="request">The incoming RST, can be used to obtain addtional information.</param>
    /// <param name="scope">The scope information corresponding to this request.</param>/// 
    /// <exception cref="ArgumentNullException">If 'principal' parameter is null.</exception>
    /// <returns>The outgoing claimsIdentity to be included in the issued token.</returns>
    protected override IClaimsIdentity GetOutputClaimsIdentity( IClaimsPrincipal principal, RequestSecurityToken request, Scope scope )
    {
        if ( null == principal )
        {
            throw new ArgumentNullException( "principal" );
        }

        ClaimsIdentity outputIdentity = new ClaimsIdentity();

        // Issue custom claims.
        // TODO: Change the claims below to issue custom claims required by your application.
        // Update the application's configuration file too to reflect new claims requirement.

        outputIdentity.Claims.Add( new Claim( System.IdentityModel.Claims.ClaimTypes.Name, principal.Identity.Name ) );
        outputIdentity.Claims.Add( new Claim( ClaimTypes.Role, "Manager" ) );

        return outputIdentity;
    }

4º Probando el escenario

Ahora, para probar nuestro escenario, solamente tenemos que crear un cliente cualquiera que ataque a nuestros servicios y si, no hemos comentido ningun error, podremos ver como la llamada al servicio funciona correctamente y la validación y obtención de claims se realiza correctamente. Con el fin de mostrar en el servicio las claims de ejecución en cada uno de los métodos utilizaremos en ellos una llamada a un método llamado CheckSecurity, que por ahora, solamente tendrá el propósito de enseñarnos por pantalla los claims del usuario autenticado.

 

       void CheckSecurity()
        {
            //TODO:Check security here!

            IClaimsPrincipal principal = Thread.CurrentPrincipal as IClaimsPrincipal;
            if (principal != null)
            {
                Console.WriteLine("User claims collection");
                foreach (var claim in ((IClaimsIdentity)principal.Identity).Claims)
                {
                    Console.WriteLine("t Name:{0} Value:{1}",claim.ClaimType,claim.Value);
                }
            }
        }

En el siguiente post veremos como modificar la parametrización del servicio para utilizar otro tipo de autenticación y alguna carcterística a mayores que pueden ser interesantes como desarrolladores.

 

El código de ejemplo correspondiente a esta entrada puede descargarse desde aquí.

 

Espero que os sirva de interés

Unai Zorrilla Castro

EF 4.1: Version-Stamping

Como seguramente muchos sabréis, la mayoria de los motores de base de datos proveen de un sistema de version-stamping para el marcado de las filas de una tabla. Con este tipo de sistemas, cada vez que se realiza una operación sobre una fila, al estilo de inserción/actualización, la columna marcada para hacer version-stamping se modifica con un valor único, lo cual, nos podría proporcionar un sistema de manejo de concurrencia realmente sencillo ( aunque lógicamente PENALIZANDO EL MODELO al introducir en la entidad un artefacto que nada tiene que ver con ella). En Sql Server, el tipo de datos a utilizar para version-stamping es timestamp ( también conocido por su sinónimo rowversion).  Pues bien, EF 4.1 nos permite el uso de este tipo de datos de una forma realmente sencillo por medio del uso de un atributo o bien por medio del API fluent para el mapeo de entidades.

 

Con el fin de ver un sencillísimo ejemplo partiremos de una entidad definida como podemos ver en el siguiente fragmento de código:

 

public class Customer
{
   public int CustomerId { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }

   [Timestamp()]
   public byte[] RowVersion { get; set; }

}

Como puede observar, la propidad ( podríamos hacerla privada ) está decorada con el atributo Timestamp y, puesto que el tipo inherente en el caso de Sql Server es un binario ( 8 bytes ) el tipo de datos en .NET es Byte[]. Con esta sencilla decoración, nuestras operaciones de borrado/actualización harán uso de este valor para comprobar si la fila sobre la que estamos actuando ha sido modificada/borrada previamente. En el siguiente fragmento SQL puede ver como sería una actualización sobre la entidad anterior:

 

 

exec sp_executesql N'update [dbo].[Customers]
set [FirstName] = @0
where (([CustomerId] = @1) and ([RowVersion] = @2))
select [RowVersion]
from [dbo].[Customers]
where @@ROWCOUNT > 0 and [CustomerId] = @1',N'@0 nvarchar(max) ,@1 int,@2 binary(8)',@0=N'unai',@1=1,@2=0x00000000000007D2

Observe, al igual que con las propiedades identity en las inserciones, después de realizar la operación de actualización se actualiza el valor de esta propiedad establecido automáticamente en la base de datos.

 

Por supuesto ( y como debería ser ) esta anotación puede establecerse directamente en nuestor api fluent, para ello, solamente tendremos que utilizar el método IsRowVersion.

 

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<Customer>()
               .Property(p => p.RowVersion)
               .IsRowVersion();
}

 

 

Saludos

unai

MongoDB liberada la versión 1.8

Esta es una de esas cosas que se te van quedando en la cartera, pero no quería dejar escapar la ocasión. Aunque mi blog es técnico y no me gusta hacer de “RSS dispatcher”, creo que MongoDB se merece que ponga esta entrada. Sobre todo, porque además  comentaré alguna otra cosa que os puede interesar.

Como reza el título, la version 1.8 de Mongo DB acaba de ser liberada este marzo, y, de entre las muchas features ( también hay bastante bug fixing) podemos destacar journaling y el soporte de búsquedas  en espacios esféricos ( anteriormente solamente se podía en plano, con funciones estilo near). Para todos los que queráis saber más acerca de las novedades que esta nueva versión nos ofrece les recomiendo este Webminar. Como sorpresa Mongo DB también acaba de anunciar el soporte de la versión de su driver 1.0 para C#, algo esperado sin dudas por muchos de nosotros que ya utilizábamos las versiones preliminares.

 

Para terminar, a todos aquellos que estéis interesados en Mongo DB en particular, os comentó que dentro de poco tendremos en Channel9 Spain unos cuantos web cast de un servidor, a petición de nuestro amigo David Salgado, Microsoft DPE España.

 

P.D: No quiero dejar escapar la ocasión de recomendaros el último libro de Kristina Chodorow, Scaling Mongo DB

 

Klingon Note:Klingon function calls do not have ‘parameters’ – they have ‘arguments’ — and they ALWAYS WIN THEM

 

Saludos

Unai Zorrilla