Applying Code Access Restrictions in .NET

Sometimes there is the need or desire to resort to Aspect-Oriented frameworks (AOP) in order to add some validations or cross-cutting concerns. In several situations, it is indeed necessary, but .NET already offers some hooks that we can use to achieve parts of this functionality. One of those is CodeAccessSecurityAttribute. In a nutshell, it allows the application of security checks whenever some code – method, property – is called. A somewhat well known example is PrincipalPermissionAttribute.

Let’s see an example of how we can restrict a method so that it can only be called on a specific day of the week:

   1: [Serializable]
   2: public sealed class DayOfWeekPermission : IPermission
   3: {
   4:     public DayOfWeek AllowedDayOfWeek { get; private set; }
   5:  
   6:     public DayOfWeekPermission(DayOfWeek allowedDayOfWeek)
   7:     {
   8:         this.AllowedDayOfWeek = allowedDayOfWeek;
   9:     }
  10:  
  11:     #region IPermission Members
  12:  
  13:     public IPermission Copy()
  14:     {
  15:         return(new DayOfWeekPermission(this.AllowedDayOfWeek));
  16:     }
  17:  
  18:     public void Demand()
  19:     {
  20:         new SecurityPermission(SecurityPermissionFlag.ControlPrincipal).Assert();
  21:  
  22:         if (this.AllowedDayOfWeek != DateTime.Today.DayOfWeek)
  23:         {
  24:             throw (new SecurityException("Cannot run on the current day of week") { Action = SecurityAction.Demand, FirstPermissionThatFailed = this, DenySetInstance = this });
  25:         }
  26:     }
  27:  
  28:     public IPermission Intersect(IPermission target)
  29:     {
  30:         return (null);
  31:     }
  32:  
  33:     public Boolean IsSubsetOf(IPermission target)
  34:     {
  35:         return (false);
  36:     }
  37:  
  38:     public IPermission Union(IPermission target)
  39:     {
  40:         return (this);
  41:     }
  42:  
  43:     #endregion
  44:  
  45:     #region ISecurityEncodable Members
  46:  
  47:     public void FromXml(SecurityElement e)
  48:     {
  49:         if (e.Tag == "AllowedDayOfWeek")
  50:         {
  51:             this.AllowedDayOfWeek = (DayOfWeek) Enum.Parse(typeof(DayOfWeek), e.Text, true);
  52:         }
  53:     }
  54:  
  55:     public SecurityElement ToXml()
  56:     {
  57:         return (new SecurityElement("AllowedDayOfWeek", this.AllowedDayOfWeek.ToString()));
  58:     }
  59:  
  60:     #endregion
  61: }
  62:  
  63: [Serializable]
  64: [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
  65: public sealed class DayOfWeekPermissionAttribute : CodeAccessSecurityAttribute
  66: {
  67:     public DayOfWeek AllowedDayOfWeek { get; set; }
  68:  
  69:     public DayOfWeekPermissionAttribute(SecurityAction action) : base(action)
  70:     {
  71:     }
  72:  
  73:     public override IPermission CreatePermission()
  74:     {
  75:         return (new DayOfWeekPermission((this.AllowedDayOfWeek)));
  76:     }
  77:  
  78:     public override Boolean Equals(Object obj)
  79:     {
  80:         var dowp = obj as DayOfWeekPermissionAttribute;
  81:  
  82:         if (dowp == null)
  83:         {
  84:             return (false);
  85:         }
  86:  
  87:         return (dowp.AllowedDayOfWeek == this.AllowedDayOfWeek);
  88:     }
  89:  
  90:     public override Int32 GetHashCode()
  91:     {
  92:         return (this.AllowedDayOfWeek.GetHashCode());
  93:     }
  94: }

The Intersect, Union and IsSubsetOf methods really don’t apply in this simple case, but the Demand method does: this is where the actual logic goes. The example should be pretty straightforward. One thing to keep in mind, though: classes inheriting from CodeAccessSecurityAttribute should expose a public constructor with a single SecurityAction parameter, otherwise, it won’t be called.

The attribute can be applied as:

   1: [DayOfWeekPermission(SecurityAction.Demand, AllowedDayOfWeek = DayOfWeek.Monday)]
   2: public void MethodToRunOnMondays()
   3: {
   4:     //if today is not Monday, an exception is thrown
   5: }

But this technique can be used for things other than permission checks. Let’s see logging applied to a class:

   1: [Serializable]
   2: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
   3: public sealed class LogPermissionAttribute : CodeAccessSecurityAttribute
   4: {
   5:     public LogPermissionAttribute(SecurityAction action) : base(action)
   6:     {
   7:     }
   8:  
   9:     public override IPermission CreatePermission()
  10:     {
  11:         return (new LogPermission());
  12:     }
  13:  
  14:     public override Boolean Equals(Object obj)
  15:     {
  16:         return (obj is LogPermissionAttribute);
  17:     }
  18:  
  19:     public override Int32 GetHashCode()
  20:     {
  21:         return (base.GetHashCode());
  22:     }
  23: }
  24:  
  25: [Serializable]
  26: public sealed class LogPermission : IPermission
  27: {
  28:  
  29:     #region IPermission Members
  30:  
  31:     public IPermission Copy()
  32:     {
  33:         return (new LogPermission());
  34:     }
  35:  
  36:     public void Demand()
  37:     {
  38:         var stackTrace = new StackTrace();
  39:         var frames = stackTrace.GetFrames().Skip(2);
  40:         var callingMethod = frames.First().GetMethod();
  41:         System.Console.WriteLine("Calling method {0}", callingMethod);
  42:     }
  43:  
  44:     public IPermission Intersect(IPermission target)
  45:     {
  46:         return (null);
  47:     }
  48:  
  49:     public Boolean IsSubsetOf(IPermission target)
  50:     {
  51:         return (false);
  52:     }
  53:  
  54:     public IPermission Union(IPermission target)
  55:     {
  56:         return (this);
  57:     }
  58:  
  59:     #endregion
  60:  
  61:     #region ISecurityEncodable Members
  62:  
  63:     public void FromXml(SecurityElement e)
  64:     {
  65:     }
  66:  
  67:     public SecurityElement ToXml()
  68:     {
  69:         return (new SecurityElement("LogPermission", String.Empty));
  70:     }
  71:  
  72:     #endregion
  73: }

Yes, I know… it really has no access to method parameters, and it really doesn’t anything really productive, it’s just an example, remember! Winking smile

An example of its usage:

   1: [LogPermission(SecurityAction.Demand)]
   2: public class MyClass
   3: {
   4:     //all method calls will be logged
   5: }

If you have other usages, please send them to me, I’ll make sure to refer where they came from! Smile

                             

No Comments

Add a Comment

As it will appear on the website

Not displayed

Your website