Proxy Pattern İle İzin Yönetimi
Bu blogta transparent proxy ile method bazlı yetkilendirmenin nasıl yapılabileceğini anlatacağım. Öncelikle problemimizden bahsetmek istiyorum daha sonrada çözüm yollarını araştıracağız. İş katmanında yazılan kodlara yetki kontrolü eklemek istiyorum. Bunun için her metoda tek tek girip sen şu roller için çalışacaksın demek istemiyorum. Fakat methodların hangi rollerde olduğuna dair bilgi üzerindeki "attributeler" de mevcuttur. Benim yapmam gereken bu methodlar çağrılmadan önce yetki kontrolü yapmak, eğer yetkisiz erişim mevcutsa hata dönderip methodu çalıştırmamak. Yetki kontrolünü tek bir merkezden yapmak istiyorum. Kısacası iş katmanı sınıflarındaki methodların önüne geçip yetki kontrolu yapmak bizim problemimiz.
Bu problemi çözmek için reflection kullanılabilir, fakat zaten bunu bizim yerimize yapan bir sınıf var. RealProxy sınıfı MarshalByRefObject sınıfından miras alınarak oluşturulmuş sınıfları açabiliyor ve bize gerekli kodlamayı eklememize izin veriyor. Sonunda o sınıf gibi fakat kodlarımızın eklendiği bir sınıf dönderebiliyoruz. Buna microsoft TransparentProxy adını koymuş.
Öncelikle herhangi bir sınıfı içine alıp TransparentProxy (aynı sınıfın düzenlenmişini) sini dönderen generic bir method yazalım.
public class GenericProxyBuilder<TProxyObj> : RealProxy where TProxyObj : new()
{
TProxyObj proxyObj;
public GenericProxyBuilder(TProxyObj proxyObj)
: base(typeof(TProxyObj))
{
this.proxyObj = proxyObj;
}
public GenericProxyBuilder() { }
public static TProxyObj GetInstance()
{
return (TProxyObj)new GenericProxyBuilder<TProxyObj>(new TProxyObj()).GetTransparentProxy();
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage mcm = (IMethodCallMessage)msg;
IMessage rtnMsg = null;
if (mcm != null)
{
rtnMsg = new ReturnMessage(mcm.MethodBase.Invoke(proxyObj,
mcm.InArgs), null, 0, mcm.LogicalCallContext, mcm);
}
return rtnMsg;
}
}
Bu sınıf tip olarak içine aldığı sınıftan TransparentProxy oluşturuyor. Abstract olan Invoke metodu ile o sınıfa ait tüm methodların arasına girmiş olacağız. Şimdi de örnek bir iş katmanı sınıfı yazalım. Bu sınıf MarshalByRefObject sınıfından miras almak zorundadır.
public class BussinessLayerCodes:MarshalByRefObject
{
public void Method1()
{
}
}
class Program
{
static void Main()
{
var instance = GenericProxyBuilder<BussinessLayerCodes>.GetInstance();
instance.Method1( );
}
}
Bu kodu çalıştırdığımızda "Araya girdik." çıktısından sonra kodumuzun çalıştığını göreceksiniz. Şimdi bu özelliği izin yönetiminide nasıl kullanacağız onu gösterecem.
Öncelikle bir tane attribute tanımlayıp Method1'in üzerine koyup bu methodu işaretleyelim.
class RoleAttribute:Attribute
{
public string Role { get; set; }
}
public class BussinessLayerCodes:MarshalByRefObject
{
[PermissionAttribute(Role="Manager")]
public void Method1()
{
}
}
public class GenericProxyBuilder<TProxyObj> : RealProxy where TProxyObj : new()
{
TProxyObj proxyObj;
public GenericProxyBuilder(TProxyObj proxyObj)
: base(typeof(TProxyObj))
{
this.proxyObj = proxyObj;
}
public GenericProxyCreator() { }
public static TProxyObj GetInstance(Action<MethodBase> after = null, Action<MethodBase> before=null)
{
return (TProxyObj)new GenericProxyBuilder<TProxyObj>(new TProxyObj()) { Before = before ?? delegate { }, After = after
?? delegate {
} }.GetTransparentProxy();
}
private Action<MethodBase> After { get; set; }
private Action<MethodBase> Before { get; set; }
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage mcm = (IMethodCallMessage)msg;
IMessage rtnMsg = null;
if (mcm != null)
{
Before(mcm.MethodBase);
rtnMsg = new ReturnMessage(mcm.MethodBase.Invoke(proxyObj,
mcm.InArgs), null, 0, mcm.LogicalCallContext, mcm);
After(mcm.MethodBase);
}
return rtnMsg;
}
}
public class BussinessCodeCaller<BussinessLayerObject> where BussinessLayerObject : new()
{
BussinessLayerObject bussinessObj;
public BussinessCodeCaller(BussinessLayerObject bussinessObj)
{
this.bussinessObj = bussinessObj;
}
static String[] DemoAuthenticatedUserRoles = { "Manager", "Director" };
public static void ControlRoles(MethodBase mcm)
{
var HasRole = mcm.GetCustomAttributes(true).Where(x => x is RoleAttribute
&& DemoAuthenticatedUserRoles.Contains(((RoleAttribute)x).Role)).Any();
if (HasRole == false)
throw new Exception("Authentication
failure!");
}
public static void Logging(MethodBase mb)
{
//loglama
yapılabilir.
}
public static BussinessLayerObject Instance
{
get
{
return GenericProxyBuilder<BussinessLayerObject>.GetInstance(before:ControlRoles,after:Logging);
}
}
}
Yazdığımız kodları test edelim. Aşagıdaki gibi çağıralım.
class Program
{
static void Main()
{
var instance = BussinessCodeCaller<BussinessLayerCodes>.Instance;
instance.Method1();
}
}