EntLib'i Genişletmek 1

Giriş

EntLib (Microsoft Enterprise Library ) yayınlanalı iki aydan fazla bir süre oldu. EntLib'i size herbirinde bir eklenti geliştireceğimiz bir yazı dizisiyle daha yakından tanıtmak istiyorum. İlk yazıda Security Block'u LDAP'tan kimlik doğrulama için genişletiyoruz.

EntLib nedir?

EntLib kurumsal uygulamalar için Microsoft Patterns & Practices Group ve Avanade tarafından ortaklaşa geliştirilmiş açık kaynaklı bir altyapı kütüphaneleri seti. Yayınlanalı iki aydan fazla bir süre oldu. Aslında EntLib daha önce de aşina olduğumuz çeşitli uygulama bloklarının biriken deneyimlerle geliştirilmiş ve biraraya toparlanmış hali. Bu bloklar;

  • Konfigürasyon bloğu (en alt katman)

  • Şifreleme bloğu

  • Önbellek bloğu

  • Loglama bloğu

  • Hata yönetim bloğu

  • Veri erişim bloğu

  • Güvenlik bloğu (en alt katman)


Nerdeyse tüm uygulamaların geliştirilmesinde gündeme gelen bu konulara standart, ortak ve genişletilebilir çözümler sunan bu kütüphaneler, yazılım geliştirme sürecini hem disipline ediyor hem de oldukça kolaylaştırıyor.

Kendi kimlik doğrulama sağlayıcımızı yazmak

EntLib geliştirilirken kurumların bu kütüphaneleri genişletme ihtiyacı gözönünede tutulmuş ve bazı genişletme noktaları bırakılmış. Kimlik doğrulama da bunlardan biri. Bu yazıda EntLib’in güvenlik uygulama bloğunu kendi Ldap sunucumuzdan kimlik doğrulamak üzere genişleteceğiz. Bunun için yeni bir sınıf kütüphanesi oluşturmamız gerekiyor. Temelde Yapmamız gereken işlem Security kütüphanesi içindeki IAuthenticationProvider adlı arayüzü LDAP bağlantısı ile sağlayan bir sınıf geliştirmek. Arayüzümüz oldukça basit;


public interface IAuthenticationProvider : IConfigurationProvider
{
bool Authenticate(object credentials, out IIdentity identity);
}


Ancak, bu arayüzün Configuration bloğundan gelen IConfigurationProvider arayüzünü genişlettiğini görüyoruz. Bu arayüz ise;


public interface IConfigurationProvider
{
string ConfigurationName { get; set; }
void Initialize(ConfigurationView configurationView);
}


Evet sonuçta uymamız gereken arayüz kontratının üç üyesi bulunuyor. Sınıfımızın çatısı şöyle birşey olmalı;


public class LdapAuthProvider : IAuthenticationProvider
{
private string _confName;
string ConfigurationName
{
get{return _confName;}
set{_confName=value;}
}
void Initialize(ConfigurationView configurationView)
{
// init.
}
bool Authenticate(object credentials, out IIdentity identity)
{
//impl.
}
}


Initialize metodunun içerisinde uygulama konfigürasyon yapısından bize ait konfigürasyon bölümünü tespit ediyoruz.


Logger.Write("Initializing LDAPAuthProvider : "+this.ConfigurationName);
ArgumentValidation.CheckForNullReference(configurationView,"configurationView");
ArgumentValidation.CheckExpectedType(configurationView, typeof(SecurityConfigurationView));
this.securityConfigurationView=(SecurityConfigurationView)configurationView;
Logger.Write("LDAPAuthProvider : "+this.ConfigurationName+" initialized");


Daha sonra bu nesneyi kullanarak konfigürasyondan LDAP sunucu adını ve kullanıcıları soruşturacağımız path’i okuyacağız. Dikkat edin, bu bilgileri bu noktada okumuyoruz, çünkü okusaydık uygulama çalıştıktan sonra yapılan değişikliklerden haberdar olamazdık. Oysa ki konfigürasyon bloğu bizim için konfigürasyon dosyalarını sürekli gözlüyor ve değişiklik durumunda yeniden yüklüyor.
Bu nedenle yapmamız gereken konfigürasyon bilgilerini kendi nesnelerimiz içinde saklamayıp her gerektiğinde – yani Authenticate metodunun başında - konfigürasyon bölümünden okumak. (Tabii bunun performans dezavantajı olacaktır.)


CustomAuthenticationProviderData config = (CustomAuthenticationProviderData) securityConfigurationView.GetAuthenticationProviderData(ConfigurationName);


Authenticate metodu ise asıl işimiz olan LDAP bağlantısını kuracağımız ve kullanıcı kimliğini doğrulayacağımız kısım. Metodumuza geçen credentials parametresinin object tipinde olmasının sebebi uygulama tarafından değişik tiplerde kimlik bilgilerinin gönderilebilmesi. Bu da uygulamalar ile kütüphane arasında gevşek bağlı (loosely-coupled) esnek bir ilişki kurulmasını sağlıyor. Herneyse biz girdi olarak Microsoft.Practices.Security aduzayında bulunan NamePasswordCredential tipindeki kimlik bilgilerini kabul edelim.
Gelen kimlik bilgilerine ilgili kontroller uyguladıktan sonra LDAP sunucusuna bağlanıp kullanıcının adını okumamız gerekiyor.
Authenticate metodunda dikkatinizi çekebilecek bir diğer nokta da yapılan SecurityAuthentication...Event.Fire çağrıları. Bu çağrılar, güvenlik bloğunun performans sayaçlarına gereken verileri beslemek için.
Girdi parametrelerinin geçerlemesi için Common kütüphanesinden çıkan ArgumentValidation sınıfının statik metodlarını tavsiye ediyorum.


//Cast and validate credentials
ArgumentValidation.CheckForNullReference(credentials,"Credentials");
ArgumentValidation.CheckExpectedType(credentials,typeof(NamePasswordCredential));
NamePasswordCredential cred = credentials as NamePasswordCredential;
ArgumentValidation.CheckForEmptyString(cred.Name, "Credential.Name");


Konfigürasyon ayarları

Sıra geldi, konfigürasyon dosyasının düzenlenmesine. Bu işin en kolay kısmı. EntLib kurulumunda gelen konfigürasyon aracı ile bu düzenlemenin tamamı yapılabiliyor. Uygulamanıza ait konfigürasyon dosyasını açın, uygulamaya sağ tuş yapıp New->Security Application Block deyin.
EntLib Config size yeni bir güvenlik bloğu ve yanında eğer halehazırda kullanmıyorsanız konfigürasyon bloğu yaratır. Authnetication Provider düğümünde tekrar sağ tuş yaparak New->Custom Authentication Provider yaratın. Daha sonra bu düğümü seçin. Custom Authentication Provider'ın TypeName, Extensions ve Name alanlarından oluştuğunu göreceksiniz. TypeName alanında, bizim kimlik doğrulama sınıfımız ı atayın, TypeSelector formunda alt kısımdaki düğme ile kütüphanemizi yükleyip sonra da sınıfımızı seçebilirsiniz. Extensions kolleksiyonunda ise sınıfımızda konfigürasyondan okumaya çalıştığımız 'ldapServer' ve 'Path' adlı birer anahtar yaratarak ilgili değerleri girin. Bunlar 'myldapServer' ve 'ou=People,dc=my,dc=domain' gibi değerler olmalı.

Evet, böylece geliştirmemizi tamamladık. LDAP'a ilişkin kısımlara değinmeye gerek görmedim. Aşağıda sınıfın kodlarının tamamını veriyorum. Yorum, öneri ve problemlerinizi çekinmeden yazın. Ayrıca faydalanabileceğiniz linkler;


LDAPAuthProvider sınıfı

using System;
using Microsoft.Practices.EnterpriseLibrary.Common;
using Microsoft.Practices.EnterpriseLibrary.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Security;
using Microsoft.Practices.EnterpriseLibrary.Security.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Security.Instrumentation;
using System.DirectoryServices;
using System.Security.Principal;
namespace EntLibEx.Security
{
///
/// LDAPAuthProvider provides authentication functionality against an LDAP server.
///

public class LDAPAuthProvider : ConfigurationProvider, IAuthenticationProvider
{
//Configuration parameter names
private const string LDAP_PARAM = "ldapServer";
private const string PATH_PARAM = "path";
private SecurityConfigurationView securityConfigurationView;
public LDAPAuthProvider(){}
public bool Authenticate(object credentials, out IIdentity identity)
{
identity=null;
//Cast and validate credentials
ArgumentValidation.CheckForNullReference(credentials,"Credentials");
ArgumentValidation.CheckExpectedType(credentials,typeof(NamePasswordCredential));
NamePasswordCredential cred = credentials as NamePasswordCredential;
ArgumentValidation.CheckForEmptyString(cred.Name, "Credential.Name");
//get configuration
CustomAuthenticationProviderData config = (CustomAuthenticationProviderData)securityConfigurationView.GetAuthenticationProviderData(ConfigurationName);
SecurityAuthenticationCheckEvent.Fire(cred.Name);
try
{
//LDAP query
DirectoryEntry de = new DirectoryEntry(
String.Format("LDAP://{0}/{1}",config.Extensions[LDAP_PARAM], config.Extensions[PATH_PARAM]),
String.Format("uid={0},{1}", cred.Name, config.Extensions[PATH_PARAM]),
cred.Password,
AuthenticationTypes.ServerBind);
DirectorySearcher ds = new DirectorySearcher(de);
ds.Filter = String.Format("(uid={0})", cred.Name);
ds.PropertiesToLoad.AddRange(new string[]{"givenname"});
SearchResultCollection sr=null;
sr = ds.FindAll();
ArgumentValidation.CheckForNullReference(sr,"SearchResultCollection");
if(sr.Count == 0)
{
SecurityAuthenticationFailedEvent.Fire(cred.Name);
return false;
}
identity = new GenericIdentity(de.Properties["givenname"].Value.ToString());
return true;
}
catch(Exception exc)
{
SecurityAuthenticationFailedEvent.Fire(cred.Name);
Logger.Write(String.Format("Authentication of user{0} failed. Cause :{1}",cred.Name,exc.Message));
return false;
}
}
#region IConfigurationProvider Members
public override void Initialize(ConfigurationView configurationView)
{
Logger.Write("Initializing LDAPAuthProvider : "+this.ConfigurationName);
ArgumentValidation.CheckForNullReference(configurationView, "configurationView");
ArgumentValidation.CheckExpectedType(configurationView, typeof(SecurityConfigurationView));
this.securityConfigurationView = (SecurityConfigurationView)configurationView;
Logger.Write("LDAPAuthProvider : "+this.ConfigurationName+" initialized");
}
#endregion
}
}


NOT : Eğer loglama bloğunu kullanmıyorsanız LdapAuthProvider sınıfındaki loglama satırlarını yoruma çevirmelisiniz.


bioLogic tarafından 21.04.2005 tarihinde yazılmıştır.

Labels: , ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home