Cercar en aquest blog

27/4/10

La capa de dades: Entity Framework

Per fer una petita introducció, recomano la lectura de les següents entrades de la llinreria MSDN.



Un exemple aclaridor de la potencia d'aquest framework la podem veure en una de les seccions a la que ens guia el segon enllaç, "Walkthrough: Serialize Self-Tracking Entities (Entity Framework)" o "Attaching and Detaching Objects (Entity Framework)" o "Working with Self-Tracking Entities (Entity Framework)".

I també estaria bé fer una lectura del que no es recomana a l'hora de fer aplicacions de n-capes, "Anti-Patterns To Avoid In N-Tier Applications", i el que si està bé "N-Tier Application Patterns"







Aprenem més...........!!!

22/4/10

Proveïdor d’Identitats: Un exemple per entendre-ho tot (VI)

(PART V)

Bé doncs, ara implementarem la segona part, entrar des del servei fronterer al servei final, i que aquest rebi les credencials de l’usuari que ha iniciat el procés en l’estació client.


Primer de tot, editarem el fitxer de configuració del servei fronterer i afegirem el següent:

 
Després agreguem una referència al servei final.
 
 
I, tornant al web.config del servei fronterer apliquem la següent modificació:
 
Tot seguit modifiquem la implementació d’ambdós serveis, per tal d’aplicar la funcionalitat que volem demostrar.





En el servei fronterer (Front_End_Service):

public string GetDataUserName()
        {
            //TODO: Change the code below to handle your claims usage.
            IClaimsPrincipal principal = (IClaimsPrincipal)Thread.CurrentPrincipal;
            IClaimsIdentity identity = (IClaimsIdentity)principal.Identity;

            SecurityToken st = identity.BootstrapToken;
            if (st == null)
            {
                st = principal.Identities[0].BootstrapToken;
            }

            string _sconf = "WS2007FederationHttpBinding_IService";
            RequestSecurityTokenResponse _rsts = new RequestSecurityTokenResponse();

            STSRPClient c2 = new STSRPClient(st, _sconf);

            Back_End_Service.IServiceChannel cl2 = c2.ClientActAs;

            string res1 = cl2.GetDataUserName();

            cl2.Close();

            return string.Format("Front_End_Service: tu ets {0}{1}{2}" , identity.Name, "\r\n" ,res1);
        }


I en el servei final (Back_End_Service):
public string GetDataUserName()
        {
            //TODO: Change the code below to handle your claims usage.
            IClaimsPrincipal principal = (IClaimsPrincipal)Thread.CurrentPrincipal;
            IClaimsIdentity identity = (IClaimsIdentity)principal.Identity;

            return string.Format("Back_End_Service: tu ets {0} i l'actor és {1}", identity.Name, identity.Actor.Name);
        }


I no oblidem importar les llibreries IDP i STS al servei fronterer.


Ara només queda accedir a la consola del ADFS 2.0 i configurar el servei final per tal de que accepti delegació. Per fer-ho, haurem de donar autorització per delegar a l’usuari del pool de connexions on es trobi el servei fronterer dins el servidor d’aplicacions (IIS+WAS). Normalment el servei de xarxa, tot i que no és el més convenient, si volem fer crides des de servidors diferents. És millor utilitzar un usuari del domini, amb permisos d’accés (els necessaris) al AD.
Primer de tot, dins la consola, editem les reclamacions del servei final:

Tot seguit ens posicionem en la pestanya de regles d’autorització de delegació.




Afegim la regla que autoritzarà a l'usuari del pool d'aplicacions del servei fronterer a l' IIS a actuar com a l'usuari que hi ha encaixat en el token que s'ha generat des de la estació client per accedir al servei fronterer i que també ens servirà per accedir al servei final. Això és el que s'anomena "delegar el control l'accés".


L'usuari del servei de xarxa, és el que i ha configurat al pool del servei fronterer a l'IIS. Quan un servei o aplicació té configurat aquest usuari en el seu pool, i accedeix a un recurs de xarxa, l'usuari es converteix dins el domini en l'usuari [domini\nomdehost$], molt important saber-ho. Una altre cosa que hem de fer, es donar permisos d'accés a la clau privada del certificat que utilitzem per signar els tokens a aquest usuari.

Tot seguit, un cop afegida la regla, ens posicionem a les regles de transformació (primera pestanya). Esborrem tot el que hi hagi, i hi afegim dues regles que deixarem passar “reclamacions” del token d’entrada, el que utilitzem per accedir al servei de forma delegada.






Fem el mateix amb el Rol, i finalment tenim:




I ja està, ara només cal provar un altre cop tot el procés, provem i obtenim:



Finalment podem veure com fem traspassar de manera transparent, les credencials de l’usuari fins al servei final. Com que no podem fer un login de l’usuari en el servei fronterer (no podem, desconeixem la contrasenya de l’usuari) utilitzem autoritzacions de delegació per tal d’aconseguir-ho.



Amb aquest senzill exemple, es demostra una de les grans utilitats que pot oferir un gestor d’identitats.

Proveïdor d’Identitats: Un exemple per entendre-ho tot (V)


(PART IV)
Per implementar la negociacio amb el STS i el accés al servei, primer ens crearem un a llibreria en C# i anomenada IDP i li posarem aquesta classe dins: 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IdentityModel.Tokens;
using System.ServiceModel;
using Microsoft.IdentityModel.Protocols.WSTrust;
using System.ServiceModel.Security;

namespace IDP
{
    public static class EndPoints
    {
        public static string baseUri = "http://[idphost]/adfs/services/";
        public static string SSLbaseUri = "https://[idphost]/adfs/services/";
        
        public  static SecurityToken GetTokenFrom_trust_13_usernamemixed(string username, string password, string appliesTo, out RequestSecurityTokenResponse rsts)
        {
            string adrecaSTS = "trust/13/usernamemixed";

            WS2007HttpBinding binding = new WS2007HttpBinding();

            binding.Security.Message.EstablishSecurityContext = false;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
            binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
            binding.Security.Mode = SecurityMode.TransportWithMessageCredential; //https




            WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(binding, new EndpointAddress(SSLbaseUri + adrecaSTS));
            trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
            trustChannelFactory.Credentials.UserName.UserName = username;
            trustChannelFactory.Credentials.UserName.Password = password;
            trustChannelFactory.ConfigureChannelFactory();

            WSTrustChannel tokenClient = (WSTrustChannel)trustChannelFactory.CreateChannel();


            //create a token issuance issuance
            RequestSecurityToken rst = new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue);
            //Relying Party’s identifier
            rst.AppliesTo = new EndpointAddress(appliesTo);
            //call ADFS STS
            SecurityToken token = tokenClient.Issue(rst, out rsts);

            return token;
        }
        
        public static SecurityToken GetTokenFrom_trust_13_windows(string appliesTo, out RequestSecurityTokenResponse rsts)
        {
            string adrecaSTS = "trust/13/windows";

            WS2007HttpBinding binding = new WS2007HttpBinding();

            binding.Security.Message.EstablishSecurityContext = false;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
            binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
            binding.Security.Mode = SecurityMode.Message;
            binding.Security.Message.NegotiateServiceCredential = true;




            WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(binding, new EndpointAddress(baseUri + adrecaSTS));
            trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
            trustChannelFactory.ConfigureChannelFactory();

            WSTrustChannel tokenClient = (WSTrustChannel)trustChannelFactory.CreateChannel();


            //create a token issuance issuance
            RequestSecurityToken rst = new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue);
            //Relying Party’s identifier
            rst.AppliesTo = new EndpointAddress(appliesTo);
            //call ADFS STS
            SecurityToken token = tokenClient.Issue(rst, out rsts);

            return token;
        }

  
    }
}


Aquesta classe ens ajuda a obtenir tokens del STS per un servei en concret. El primer mètode, a partir d’unes credencials entrades per l’usuari, i el segon utilitzan les credencials del usuari loginat al SO (usuari Windows).


I ara, per tal de facilitar la creació de clients de Serveis (Agents de servei), que puguin actuar amb o sense delegació, utilitzarem una altre llibreria que ens facilitarà la feina. Aquesta la farem en VB.Net i li direm STS. Dins hi posarem la següent classe. Hem de fer les referències.

Imports System.IdentityModel.Tokens
Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports Microsoft.IdentityModel.Protocols.WSTrust
Imports System.ServiceModel.Channels
Imports System.ServiceModel.Security
Imports System.ServiceModel.Security.Tokens
Imports System.Text

Public Class STSRPClient(Of T)
    Implements IDisposable
#Region "Members"
    Private _st As SecurityToken
    Private _factory As ChannelFactory(Of T)
#End Region
    ''' 
    ''' Contructor per generar Client a partir del fitxer de configuració
    ''' 
    ''' 
''' 
''' 
    Sub New(ByVal st As SecurityToken, ByVal bindingConfiguration As String)
        Create(st, bindingConfiguration)
    End Sub
  
    Private Sub Create(ByVal st As SecurityToken, ByVal bindingconfiguration As String)
        Me._st = st
        _factory = New ChannelFactory(Of T)(bindingconfiguration)
        _factory.ConfigureChannelFactory()
    End Sub

    Public Sub Close()
        _factory.Close()
    End Sub

    Public ReadOnly Property Client As T
        Get
            Return _factory.CreateChannelWithIssuedToken(_st)
        End Get
    End Property
    Public ReadOnly Property ClientActAs As T
        Get
            Return _factory.CreateChannelActingAs(_st)
        End Get
    End Property

#Region "IDisposable Support"
    Private disposedValue As Boolean ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' TODO: dispose managed state (managed objects).
            End If
            If Me._factory.State <> CommunicationState.Closed Then
                _factory.Close()
            End If
            _st = Nothing
            ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
            ' TODO: set large fields to null.
        End If
        Me.disposedValue = True
    End Sub

    ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
    'Protected Overrides Sub Finalize()
    '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    '    Dispose(False)
    '    MyBase.Finalize()
    'End Sub

    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class


Un cop compilades, farem les referències a aquestes en l’aplicació client.


  1. Obtenir token per l’usuari
  2. Crear client amb el token
  3. Invocar Servei
  4. Escriure el resultat

Imports System.IdentityModel.Tokens
Imports Microsoft.IdentityModel.Protocols.WSTrust

Public Class Form1

    Private Sub BInvoke_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BInvoke.Click

        Dim adrecaservei As String = "http://localhost/Front_End_Service/Service.svc"

        ' App.config - Nom de la configuració del binding del client
        Dim conf As String = "WS2007FederationHttpBinding_IService"


        ' Primer hem d'obtenir un token de seguretat del IDP pel servei

        Dim rsts As New RequestSecurityTokenResponse
        Dim st As SecurityToken = IDP.EndPoints.GetTokenFrom_trust_13_usernamemixed(Me.TxtUser.Text, Me.TxtPwd.Text, adrecaservei, rsts)

        ' Un cop el tenim em de fer-lo servir per accedir-hi i invocar les seves operacions

        Dim clirp As New STS.STSRPClient(Of Front_End_Service.IServiceChannel)(st, conf)
        Dim client As Front_End_Service.IServiceChannel = clirp.Client

        Dim response As String = client.GetDataUserName()

        clirp.Close()
        clirp.Dispose()


        Me.TextBox1.AppendText(response + vbNewLine)

    End Sub
End Class



Diagrama de seqüències del event del botó.

Ara ja podem compilar i provar. De moment només invoquem el primer servei.



Invocació amb unes credencials incorrectes

 
Invocació amb credencials correctes.
 

Proveïdor d’Identitats: Un exemple per entendre-ho tot (IV)

(PART III)

Obrim l ‘administrador del IIS. Podem veure els serveis del ADFS i els nostres serveis.


Primer de tot, afegim un pool nou de connexions, que funcioni amb el .NET Framework 4.0. No és necessari però evitarem problemes (que no explicaré perquè depenen de la combinació de versions de SO + IIS). Aquest nou pool és el que utilitzaran els nostres nous serveis.


Bé ara ja podem tornar al Visual Studio, i picar una mica de codi.



Primer de tot podem mirar els web.config dels serveis i esbrinar que vol dir tot el que ha posat el procés de federació. És una bona pràctica.

Un cop assabentats d’aquesta nova tecnologia, modificarem els serveis.



Primer modificarem la Interfície d ‘ambdós, perquè quedi de la següent manera:
....

    [ServiceContract]
    public interface IService
    {
        [OperationContract]
        string GetDataUserName();
    }

....


Tot seguit implementem la interfície en ambdós serveis.

Front_End_Service
using System.Threading;
using Microsoft.IdentityModel.Claims;
namespace Front_End_Service
{
    public class Service : IService
    {
         public string GetDataUserName()
        {
            IClaimsPrincipal principal = (IClaimsPrincipal)Thread.CurrentPrincipal;
            IClaimsIdentity identity = (IClaimsIdentity)principal.Identity;
            return string.Format("Front_End_Service: tu ets {0}", identity.Name);
        }
    }
}



Back_End_Service

using System.Threading;
using Microsoft.IdentityModel.Claims;
namespace Back_End_Service
{
    public class Service : IService
    {
        public string GetDataUserName()
        {
            IClaimsPrincipal principal = (IClaimsPrincipal)Thread.CurrentPrincipal;
            IClaimsIdentity identity = (IClaimsIdentity)principal.Identity;
            return string.Format("Back_End_Service: tu ets {0} i l'actor és {1}", identity.Name, identity.Actor.Name);
        }
    }
}



Després ens situarem a l’ aplicació client i crearem una referència al servei “Front_End_Service”.


Un cop fet això modificarem el fitxer “app.config”, per tal de posar el endpoint del STS que consumirà el token de seguretat.



Cercarem el “tag” següent :

 
 
I el substituirem per aquest, el trobarem comentat en el mateix app.config:
 
 
Podríem utilitzar-ne un altre, però aquest, és una bona opció. Una bona pràctica seria entendre els endpoints del STS.

Tot seguit crearem un formulari semblant a aquest, amb unes entrades de usuari i contrasenya , un botó per invocar el servei, i una consola per mostrar el resultat.

I, implementarem el codi que farà possible la invocació del servei. Sabem que necessitem un token de seguretat per accedir al servei, i el token ens l’ha de donar el STS a través del ADFS 2.0.
 

Proveïdor d’Identitats: Un exemple per entendre-ho tot (III)

(PART II)

Tot seguit anirem a la consola de gestió del ADFS 2.0 i configurarem l’autentificació del servei “Front_End_Service” el fronterer, el que dona accés al client a la capa de negoci.




Un cop entrem hem de seguir uns passos molt senzills.




Pot ser que en aquest punt tinguem problemes de “certificats”. Si es així triem la segona opció, i anem a buscar de meta dades de federació al directori del projecte.


Li posem un nom.


Li donem permís a tots el usuaris. Això vol dir que qualsevol usuari de l’ Active Directori podrà accedir al servei un cop s’hagi validat.




En el següent pas entrarem les regles d’autorització, i configurarem els “claims” o reclamacions o atributs o propietats, digueu-li com voleu, que s’enviaran des del IDP al RP, o sigui des del servei STS al RP STS, que són els nostres serveis.




Aquí li diem que envií la propietat de l’usuari SAM-Account-Name com a un “claim” de tipus Name, i els grups als que pertany com a “claims” de tipus Role.


Aquí li diem que envií la propietat de l’usuari SAM-Account-Name com a un “claim” de tipus Name, i els grups als que pertany com a “claims” de tipus Role.




Finalment tenim les regles dels claims que enviarem d'un usuari autenticat i autoritzat. En la pestanya del mig tenim les regles d'autoritzacio d'accés al servei.

Aquest procés l’hem de fer amb ambdós serveis.



Proveïdor d’Identitats: Un exemple per entendre-ho tot (II)

(PART I)

Tot seguit el que cal es federar els serveis amb el nostre IDP (identity Provider), que en el nostre cas és ADFS 2.0 RC.

 
Tot seguit la seqüència de federació del servei.
 

El “STS WS-Federation metadata document location” normalment està al servidor on hem instal•lat el ADFS 2.0:
https://[idphost]/FederationMetadata/2007-06/FederationMetadata.xml


Podem xifrar o no el token de seguretat. Si ho fem podem generar un certificat o triar-ne un existent .
Tot seguit veiem tots els tipus de “claims” que ofereix el Security Token Service (STS) a través del IDP.


Finalment, podem programar una tasca que ajusta la configuració de federació si es produeixen canvis en el IDP.



Aquest procés de federació l’hem de fer per ambdós serveis.

Proveïdor d’Identitats: Un exemple per entendre-ho tot (I)

Per fer referència a tota una arquitectura de gestió d’identitats que fan servir aplicacions actives (winforms, windows presentation foundation, windows communication services, workflow services, aplicacions RIA, windows mobile, etc) i aplicacions passives (ASP.Net, aplicacions Web en general), unificada, centralitzada i basada en estàndards, que millor que presentar un escenari i fer-ne el desenvolupament.

Com que d’exemples d’aplicacions passives n’està ple a Internet, ens centrarem en un escenari actiu.

Descripció Escenari: Suposem un domini d’aplicació format per una aplicació client desenvolupada en winforms, que accedeix a un servei distribuït (WCF), i aquest a un altre servei distribuït (WCF). Fins ara cap cosa nova. Però ara hi posarem unes petites regles:
  • L’ usuari s’haurà d’autenticar per accedir a l’aplicació winforms, al servei fronterer i al darrer. Amb un cop n’hi hauria d’haver prou, no?
  • Ha de suportar que els serveis definits estiguin en nivells (servidors) diferents, inclòs en xarxes diferents i dominis diferents (en l’exemple no ho farem per falta de recursos, però és del tot possible i fàcil de fer).
  • La transmissió de credencials entre aplicacions actives ha de ser segura.
  • Els components de seguretat que fan referència a l’autentificació, autorització i transmissió de credencials entre aplicacions han de ser transparents pel desenvolupador. Afegir els components ha de ser un automatisme de posada en producció dins un entorn empresarial, seguin les normes establertes.
Per poder desenvolupar l’ escenari, disposarem d’una serie de recursos:
  • Dos servidors amb Windows 2008 R2, IIS + WAS, Windows Identity Foundation (WIF). En un dels dos hi instal•larem AD+ADFS 2.0 RC (hi ha molta documentació per Internet). Frameworks fins al 3.5.1. Podem instal.lar el 4.0 però a hores d’ara no és una versió publicada.
  • Una estació client amb Windows 7 o XP, amb els frameworks i WIF mencionats.
  • Una eina de desenvolupament com pot ser VS2008 o VS2010 Beta o RC, instal.lada en una de les màquines anteriorment citades. (Jo personalment, instal•lo un Visual Studio a cada servidor de desenvolupament).


Si ens fixem bé en les dependències, els serveis depenen directament de la capa de seguretat per funcionar, i per tant les aplicacions client indirectament també necessiten d’aquesta per utilitzar-los.

I per tant la seqüència que haurà de seguir l’ aplicació client per accedir al servei frontera serà:

  1. Demanar usuari i contrasenya.
  2. Connectar amb la capa de seguretat, presentar les credencials d’usuari i sol•licitar un token per accedir al servei. Si està autoritzat, aquesta li servirà un token amb la informació que necessita de l’usuari el servei per funcionar, per ser operatiu.
  3. L’agent del servei (client del servei, Proxy del servei) presentarà el token a aquest, per tal de que doni accés a la invocació de les seves operacions.
  4. El servei veurà la identitat de l’usuari que està executant l’aplicació client. Si volem transmetre-la cap al servei final, em de tenir en compte algunes coses. Primer de tot, l’usuari que executa el procés del servei al servidor, és el que hi hagi definit en el pool del IIS on estigui allotjat el servei (Normalment NetWorkService). Per tant, hem de guardar el token que arriba sense desxifrar, per poder-lo utilitzar per invocar al servei final, ja que aquest porta les credencials del usuari tal i com les entén la capa de seguretat. I a l’agent del servei final li hem de dir que encara que l’usuari executor del procés sigui el NetWorkService, actuí com a l’usuari que ha iniciat a l’aplicació client. Aquesta sol•licitud de delegació la gestiona la capa de seguretat i aquesta l’ha de permetre.
  5. Finalment, al servei final hem de tenir dues identitats. Un Actor que serà el NetWorkService i un usuari principal que serà l’usuari que ha iniciat l’aplicació client. O sigui el NetWorkService actua com a l’usuari de l’aplicació client en el servei final, el qual pot està allotjat a la Xina. Transmetem una tarja d’identitat des de l’ inici fins al final.
Anem per feina: Implementació
Primer de tot creem una solució al Visual Studio (jo utilitzo el 2010). Si hem instal•lat bé el WIF SDK, tindrem uns templates i un Add-in que ens facilitaran la feina. Alhora de crear WCF Serveis preparats per treballar amb identitats i per posar-los en producció amb un gestor d’identitats (IDP) com pot ser ADFS 2.0.
Si ens hi fixem els serveis els ubiquem al IIS local. La solució final ha de tenir una aparença semblant a la de la imatge següent.


Dos serveis habilitats per treballar amb “claims” i una aplicació Winforms.

Freemarket.com Marketplace