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!
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!