[ Team LiB ] Previous Section Next Section

7.3 Extending the .NET Framework

CAS is fully extensible and allows you to create your own permission classes that integrate with the security framework to provide capabilities equivalent to the standard permission classes. The creation of custom permissions is relatively straightforward. The complexity of the identity, actions, or resources that your permission class represents defines how difficult the development task is. In the following sections, we demonstrate how to create and implement custom code-access permissions.

7.3.1 Creating Custom Code-Access Permissions

All code-access permissions must meet the following requirements:

  • Implement the IPermission interface

  • Implement the IStackWalk interface

  • Implement the ISecurityEncodable interface

  • Implement the IUnrestrictedPermission interface

  • Provide a constructor that takes a PermissionState argument

  • Be serializable

The simplest way to meet these requirements is to derive your permission class from the abstract CodeAccessPermission class, which we discussed in Section 7.2.2.1 earlier in this chapter. CodeAccessPermission declares implementation of the IPermission, IStackWalk, and ISecurityEncodeable interfaces, but only provides concrete implementations of the IStackWalk interface methods. Because the logic of the following methods depends on permission-specific state, you must provide implementations of them in your class:

IPermission methods

  • Copy

  • Intersect

  • IsSubsetOf

  • Union

ISecurityEncodable methods

  • FromXml

  • ToXml

IUnrestrictedPermision method

  • IsUnrestrictedPermission

To meet the requirements of serialization, it is usually enough to apply the SerializableAttribute to your class definition. However, if your permission class requires nonstandard serialization processing because of the complex internal state it maintains, implement the ISerializable interface.

The CodeAccessPermission class defines an InheritenceDemand for the ControlEvidence and ControlPolicy elements of the SecurityPermission class. The assembly containing your subclass of CodeAccessPermission must have these permissions, or the runtime throws a SecurityException.

To support declarative security syntax, create an attribute counterpart to your permission class. This involves creating a class that extends the System.Security.Permissions.CodeAccessSecurityAttribute class. Your class must:

  • Implement a single constructor that takes a SecurityAction argument

  • Override the CreatePermission method

  • Implement public properties that enable the configuration of the attribute's state

7.3.1.1 Designing the RadioPermission class

To demonstrate the creation and use of custom code-access permissions, imagine that we need to develop a managed library that allows an application to control a PC-based radio tuner. The radio tuner could be a hardware attachment to the PC or software that plays Internet-based radio stations; the implementation details are unimportant, but the library must allow code to:

  • Turn the radio on and off

  • Adjust the volume of the radio

  • Change the station the radio is tuned to

The goal is to create a custom code-access permission that you can use to control access to each of these actions. After all, you do not want to allow malicious code downloaded from competitive radio stations to casually change the tuning of the radio.

The best way to achieve fine-grained access control is to implement a code-access permission that represents a discrete set of actions; this is the same model used by the SecurityPermission class that we described in Section 7.2.2.2 earlier in this chapter. Because the number of possible actions is small and well-defined, this model fits your needs well. Another common approach is to implement a permission hierarchy, where permission to perform a highly trusted action gives permission to perform all lesser-trusted actions; however, the actions you need to represent in this case do not fit this model well.

To give the programmers using the custom permission flexibility, you want to provide both imperative and declarative syntax support. This will also allow application developers to build permission requests for your permission into their assemblies. Create two classes and one enumeration:

RadioPermission

The custom code-access permission that controls access to the managed library for the radio tuner

RadioPermissionAttribute

The attribute counterpart to RadioPermission that provides declarative syntax support

RadioAction

An enumeration that defines values that represent each of the controlled actions

For convenience, we include all three types in a single source file named Radio.cs (C#) and Radio.vb (Visual Basic .NET). We will build this into a library named Radio.dll, which programmers will use in the development of the managed radio tuner library, the configuration of .NET security policy, and to make permission requests in assemblies that access the managed library. Although the code is straightforward, it is lengthy, so we break the listings into manageable sections for explanation.

7.3.1.2 Defining imports and assembly scope attributes

Aside from importing the necessary namespace references, the first thing to do is to specify the assembly scope attributes AssemblyKeyFile and AssemblyVersion to give Radio.dll a strong name. Because Radio.dll contains security classes used to extend CAS, you must configure it as a fully trusted assembly in the runtime's security policy; we discuss fully trusted assemblies in Chapter 8. For this example, the AssemblyKeyFile attribute references a key file named Keys.snk, which you can generate later using the .NET Strong Name tool (Sn.exe):

# C#

using System;
using System.Reflection;
using System.Security;
using System.Security.Permissions;

[assembly:AssemblyKeyFile("Keys.snk")]
[assembly:AssemblyVersion("1.0.0.0")]

namespace OReilly.ProgrammingDotNetSecurity {

# Visual Basic .NET

Imports System
Imports System.Reflection
Imports System.Security
Imports System.Security.Permissions
 
<assembly:AssemblyKeyFile("Keys.snk")>
<assembly:AssemblyVersion("1.0.0.0")> 
 
Namespace OReilly.ProgrammingDotNetSecurity
7.3.1.3 Defining the RadioAction enumeration

RadioPermission represents control of a discrete set of actions. The RadioAction enumeration represents each of the actions to which RadioPermission controls access. RadioAction also includes a value to represent no permission and a value to represent full or unrestricted permission.

To simplify the implementation of your permission model, apply the Flags attribute to the RadioAction enumeration. The Flags attribute allows you to use the values of the RadioAction enumeration as bit fields, enabling you to manipulate and compare RadioAction values with bitwise operators. Table 7-8 lists the members of RadioAction and shows their binary and hex values. From the binary values, it is easy to see how RadioAction identifies the individual actions to which a RadioPermission object represents access.

Table 7-8. Values of the RadioAction enumeration

Value

Binary value

Hex value

Description

None

000

0

No access to perform any actions with the radio

StartStop

001

1

Permission to turn the radio on and off

Channel

010

2

Permission to change the radio's channel

Volume

100

4

Permission to change the radio's volume

All

111

7

Permission to perform any of the above actions

Finally, because you use RadioAction values inside SecurityPermission objects (which must be serializable), you also mark RadioAction as Serializable:

# C#

    // Define enumeration to represent the set of possible permissions.
    [Flags, Serializable]
    public enum RadioAction {    
        None      = 0x00,     // No permissions
        StartStop = 0x01,     // Permission to start and stop the radio
        Channel   = 0x02,     // Permission to change the channel
        Volume    = 0x04,     // Permission to change the volume
        All       = 0x07      // All permissions
    }

# Visual Basic .NET

    ' Define enumeration to represent the set of possible permissions.
    <Flags, Serializable> _ 
    Public Enum RadioAction 
        None      = &H00   ' No permissions
        StartStop = &H01   ' Permission to start and stop the radio
        Channel   = &H02   ' Permission to change the channel
        Volume    = &H04   ' Permission to change the volume
        All       = &H07   ' All permissions
    End Enum
7.3.1.4 Defining the RadioPermission class

The class declaration for RadioPermission specifies that it is sealed (C#) and NotInheritable (Visual Basic .NET). There is no need to allow anybody to extend the RadioPermission class, and it stops malicious code from subclassing RadioPermission in an attempt to subvert security restrictions.

RadioPermission extends CodeAccessPermission, implements the IUnrestricted interface, and is Serializable. The CodeAccessPermission base class already implements the functionality defined in the IStackWalk interface so you do not have to implement the Assert, Demand, Deny, and PermitOnly methods; this simplifies the development of custom code-access permissions. However, you still need to implement the permission-specific Copy, Intersect, IsSubsetOf, and Union methods defined in IPermission, and the FromXml and ToXml methods defined in ISecurityEncodable, all of which CodeAccessPermission declares as abstract.

Declare a single private data member named RadioActions of type RadioAction. This holds a bit field representing the actions to which a specific RadioPermission object grants access. Implement the Actions property to provide controlled access to the private RadioActions member:

# C#

    // Define the RadioPermission class. 
    [Serializable]
    public sealed class RadioPermission : 
        CodeAccessPermission, IUnrestrictedPermission {

        // Private member to signal permitted actions.
        private RadioAction RadioActions;

        // Property to provide access to the enabled radio actions.
        public RadioAction Actions {        
      
            get { 
                return RadioActions; 
            }            
            set {       
                if ((value & (~RadioAction.All)) != 0) {       
                    throw new 
                        ArgumentException("Inavalid RadioAction value");
                } else {                      
                    RadioActions = value;
                }
            }
        }

# Visual Basic .NET

    ' Define the RadioPermission class. 
    <Serializable> _ 
    Public NotInheritable Class RadioPermission
      Inherits CodeAccessPermission
  
        ' Private member to signal permitted actions.
        Private RadioActions As RadioAction

        ' Property to provide access to the enabled radio actions.
        Public Property Actions(  ) As RadioAction
            Get 
                Return RadioActions
            End Get
            Set (ByVal Value As RadioAction) 
                If (value And (Not RadioAction.All)) <> 0 Then
                    Throw New ArgumentException _
                    ("Inavalid RadioAction value")
                Else 
                    RadioActions = value
                End If
            End Set
        End Property

Declare two constructors for RadioPermission. All code-access permission classes that extend CodeAccessPermission must implement a constructor that takes a PermissionState argument. Passing the constructor the value PermissionState.None creates a RadioPermission that represents no permissions by setting RadioActions to RadioAction.None, whereas passing the PermissionState.Unrestricted value creates a RadioPermission object representing full permission by setting RadioActions to RadioAction.All.

Because you use a bit field to represent each permitted action, it is easy to represent an unrestricted permission simply by switching on all action bits (RadioAction.All equals 111 in binary). In other permission models, you may need to implement a separate Boolean data member to represent an unrestricted state.

The second constructor takes a member of the RadioAction enumeration, confirms that it is valid, and sets the private RadioActions member to the value of the argument:

# C#

        // Constructor that takes a PermissionState. 
        public RadioPermission (PermissionState state) {   

            if (state == PermissionState.None) {
                this.RadioActions = RadioAction.None;
            } else if (state == PermissionState.Unrestricted) {
                this.RadioActions = RadioAction.All;
            } else {
                throw new ArgumentException("state");
            }
        }

        // Constructor that takes a RadioAction specifying the set
        // of actions permitted by this RadioPermission object.
        public RadioPermission (RadioAction actions) {   
        
            // Ensure we have a valid actions value.
            if ((actions & (~RadioAction.All)) != 0) {        
                throw new ArgumentException("Inavalid RadioAction value");
            } else {                      
                RadioActions = actions;
            }
            this.RadioActions = actions;
        }


# Visual Basic .NET

        ' Constructor that takes a PermissionState. 
        Public  Sub New(ByVal state As PermissionState)
 
            If state = PermissionState.None Then
                Me.RadioActions = RadioAction.None
            Else If state = PermissionState.Unrestricted Then 
                Me.RadioActions = RadioAction.All
            Else
                Throw New ArgumentException("state")
            End If
        End Sub
 
        ' Constructor that takes a RadioAction specifying the set
        ' of actions permitted by this RadioPermission object.
        Public  Sub New(ByVal actions As RadioAction)
 
            ' Ensure we have a valid actions value.
            If (actions And (Not RadioAction.All)) <> 0 Then
                Throw New ArgumentException("Inavalid RadioAction value")
            Else 
                RadioActions = actions
            End If
            Me.RadioActions = actions
        End Sub
7.3.1.5 Implementing the IUnrestrictedPermission interface

The IUnrestrictedPermission interface defines the IsUnrestricted method, which returns a Boolean indicating whether a permission object represents an unrestricted state. In the case of RadioPermission, you can easily calculate the response by comparing the value of the private RadioActions member with the value RadioAction.All and returning the result:

# C#

        // Return whether permission represents unrestricted access.
        public bool IsUnrestricted(  )
        {
            return RadioActions == RadioAction.All;
        }


# Visual Basic .NET

        ' Return whether permission represents unrestricted access.
        Public Function IsUnrestricted(  ) As Boolean
            Return RadioActions  =  RadioAction.All
        End Function
7.3.1.6 Implementing the IPermission interface

Both the IStackWalk and IPermission interfaces define a method named Demand. The CodeAccessPermission base class implements all members of the IStackWalk interface, and you do not need to implement the Demand method from the IPermission interface, leaving you with the Copy, Intersect, Union, and IsSubsetOf methods to implement.

The Copy method returns a clone of the permission object. In the case of RadioPermission, this is a simple matter of calling one of the constructors and passing the current RadioActions value. If your permission class uses objects to represent state, Copy must perform a deep copy and create copies of all contained objects:

# C#

        // Create a copy the RadioPermission.
        public override IPermission Copy(  ) {

            return new RadioPermission(RadioActions);
        }

# Visual Basic .NET

        ' Create a copy the RadioPermission.
        Public Overrides Function Copy(  ) As IPermission
 
            Return New RadioPermission(RadioActions)
        End Function

The Intersect method returns a new IPermission that represents the logical intersection of the current RadioPermission and another. Because RadioPermission represents permission to perform a discrete set of actions, the intersection of two RadioPermission objects is the set of actions permitted by both objects. For example, if the current RadioPermission grants Volume and Channel control and the other grants only Volume control, the intersection of the two is a RadioPermission that grants only Volume control. Because the Flags attribute allows you to treat values of the RadioAction enumeration as bit fields, you can express the intersection logic using a bitwise And on the RadioActions members of the two original RadioPermission objects.

# C#

        // Return an intersection of this and another RadioPermission 
        // object.
        public override IPermission Intersect(IPermission target) {

            // Return null if the target argument is null.
            if (target == null) {        
                return null;            
            } 

            // Ensure the target argument is another RadioPermission 
            // object, if not throw an ArgumentException.
            if (target is RadioPermission) {
        
                RadioPermission r = (RadioPermission)target;
        
                // Calculate the intersection of the radio actions
                // from this and the target RadioPermission.
                RadioAction i = 
                    this.RadioActions & r.RadioActions;
           
                // Return a new RadioPermission.
                return new RadioPermission(i);
       
            } else {
                throw new ArgumentException("target");
            }
        }


# Visual Basic .NET

        ' Return an intersection of this and another RadioPermission 
        ' object.
        Public Overrides Function Intersect(ByVal target As IPermission) _
        As IPermission
 
            ' Return null if the target argument is null.
            If target Is Nothing Then
                Return Nothing
            End If
 
            ' Ensure the target argument is another RadioPermission 
            ' object, if not throw an ArgumentException.
            If TypeOf target Is RadioPermission Then
 
                Dim r As RadioPermission = _
                CType(target, RadioPermission)
 
                ' Calculate the intersection of the radio actions
                ' from this and the target RadioPermission.
                Dim i As RadioAction =  Me.RadioActions And _
                r.RadioActions 
 
                ' Return a new RadioPermission.
                Return New RadioPermission(i)      
 
            Else 
                Throw New ArgumentException("target")
            End If
        End Function

The Union method returns a new IPermission that represents the logical union of the current RadioPermission and another. For example, if one RadioPermission grants Channel control and the other grants Volume control, the union of the two is a RadioPermission that grants both Volume and Channel control. The implementation of the Union method is similar to the Intersect method, but uses a bitwise Or operator on the RadioActions members of the two original RadioPermission objects to calculate the union:

# C#

        // Return the union of this and another RadioPermission object.
        public override IPermission Union(IPermission other) {

            // Return null if the other argument is null.
            if (other == null) {  
                return null;    
            } 
  
            // Ensure the other argument is another RadioPermission object
            // if not throw an ArgumentException.
            if (other is RadioPermission) {
  
                RadioPermission r = (RadioPermission)other;
   
                // Calculate the union of the radio actions
                // from this and the other RadioPermission.
                RadioAction u = 
                    this.RadioActions | r.RadioActions;
         
                // Return a new RadioPermission
                return new RadioPermission(u);   
            } else {
                throw new ArgumentException("other");
            }
        }


# Visual Basic .NET

        ' Return the union of this and another RadioPermission object.
        Public Overrides Function Union(ByVal other As IPermission) _
        As IPermission
 
            ' Return null if the other argument is null.
            If other Is Nothing Then
                Return Nothing
            End If
 
            ' Ensure the other argument is another RadioPermission object
            ' if not throw an ArgumentException.
            If TypeOf other Is RadioPermission Then
 
                Dim r As RadioPermission = CType(other, RadioPermission)
 
                ' Calculate the union of the radio actions
                ' from this and the other RadioPermission.
                Dim u As RadioAction = Me.RadioActions _
                Or r.RadioActions 
 
                ' Return a new RadioPermission
                Return New RadioPermission(u)      
 
            Else 
                Throw New ArgumentException("other")
            End If
        End Function

The IsSubsetOf method returns a Boolean value indicating whether the current RadioPermission is a subset of the RadioPermission passed as an argument to the method. For example, if the current RadioPermission allowed Volume control, and the one specified by the argument allowed both Volume and Channel control, then the current RadioPermission is a subset of the other. As with the Intersect and Union methods, we perform the subset relationship test using bitwise operators.

# C#

        // Determines if this RadioPermission is a subset of the provided 
        // RadioPermission. 
        public override bool IsSubsetOf(IPermission target) {

            // Return false if the target argument is null.
            if (target == null) {
        
                return false;  
            } 
       
            // Ensure the target argument is another RadioPermission 
            // object, if not throw an ArgumentException.
            if (target is RadioPermission) {
       
                RadioPermission r = (RadioPermission)target;

                // Determine if the provided set of actions are
                // a subset of the current permissions actions.
                return (this.RadioActions 
                    & (~r.RadioActions)) == 0;
       
            } else {
        
                throw new ArgumentException("target");
            }
        }


# Visual Basic .NET

        ' Determines if this RadioPermission is a subset of the provided 
        ' RadioPermission. 
        Public Overrides Function IsSubsetOf _
        (ByVal target As IPermission) As Boolean
 
            ' Return false if the target argument is null.
            If target Is Nothing Then
 
                Return False
            End If
 
            ' Ensure the target argument is another RadioPermission 
            ' object, if not throw an ArgumentException.
            If TypeOf target Is RadioPermission Then
 
                Dim r As RadioPermission = _
                CType(target, RadioPermission)
 
                ' Determine if the provided set of actions are
                ' a subset of the current permissions actions.
                Return (Me.RadioActions And _
                (Not r.RadioActions)) = 0
            Else 
                Throw New ArgumentException("target")
            End If
        End Function
7.3.1.7 Implementing the ISecurityEncodable interface

The ToXml method returns a SecurityElement, which we discussed in Chapter 6. The SecurityElement contains a simple XML object model representing the state of the RadioPermission object. The runtime uses the ToString method of SecurityElement to render permission objects to XML for writing to the security policy files, which we discuss in Chapter 9. The XML representation is also useful when debugging CAS programming issues.

The XML representation of all code-access permissions must have a root element named "IPermission." The internal structure of this element is up to you, as long as the FromXml method (discussed next) can accurately recreate the state of a permission object from the XML. Here is an example of the XML used to represent a RadioPermission object that represents access to the StartStop action only.

<IPermission class="OReilly.ProgrammingDotNetSecurity.RadioPermission, 
Radio, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc5e18bc387194b3"
             version="1"
             StartStop="true"/>

The XML structure you have implemented for RadioPermission is consistent with that used by the standard permission classes. Use attributes, as opposed to child elements, to store state data, and include active state only, assuming missing values have their default values. If the RadioPermission object represents access to all actions, include a single Unrestricted = true attribute instead of including values for each of the individual actions. The version attribute identifies the XML format to provide backward compatibility in case future versions of the RadioPermission use different XML representations:

# C#

        // Convert the Radio Permission to a SecurityElement.
        public override SecurityElement ToXml(  ) {
   
            // Create a new "IPermission" element.
            SecurityElement se = new SecurityElement("IPermission");
       
            // Add fully qualified type name for the RadioPermission.
            se.AddAttribute("class",this.GetType(  ).AssemblyQualifiedName);
       
            // Add version of "1" to be consistent with other permission
            // classes
            se.AddAttribute("version", "1");
      
            // Add the radio action state, only write out active 
            // permissions.
            if (this.IsUnrestricted(  )) {
       
                // Write only the unrestricted attribute if the 
                // permission is unrestricted.
                se.AddAttribute("Unrestricted", "true");
            } else {
        
                // Write out the individual actions that are granted.
                if ((RadioActions & RadioAction.StartStop) != 0) {
                    se.AddAttribute("StartStop", "true");
                }
                if ((RadioActions & RadioAction.Channel) != 0) {
                    se.AddAttribute("Channel", "true");
                }
                if ((RadioActions & RadioAction.Volume) != 0) {
                    se.AddAttribute("Volume", "true");
                }
            }
          
            // Return the new SecurityElement.
            return se;
        }

# Visual Basic .NET

        ' Convert the Radio Permission to a SecurityElement.
        Public Overrides Function ToXml(  ) As SecurityElement
 
            ' Create a new "IPermission" element.
            Dim se As SecurityElement = New SecurityElement("IPermission") 
 
            ' Add fully qualified type name for the RadioPermission.
            se.AddAttribute("class",Me.GetType(  ).AssemblyQualifiedName)
 
            ' Add version of "1" to be consistent with other permission
            ' classes
            se.AddAttribute("version", "1")
 
            ' Add the radio action state, only write out active 
            ' permissions.
            If Me.IsUnrestricted(  ) Then
 
                ' Write only the unrestricted attribute if the 
                ' permission is unrestricted.
                se.AddAttribute("Unrestricted", "true")
            Else 
 
               ' Write out the individual actions that are granted.
               If (RadioActions And RadioAction.StartStop) <> 0 Then
                   se.AddAttribute("StartStop", "true")
               End If
               If (RadioActions And RadioAction.Channel) <> 0 Then
                   se.AddAttribute("Channel", "true")
               End If
               If (RadioActions And RadioAction.Volume) <> 0 Then
                   se.AddAttribute("Volume", "true")
                End If
            End If
 
            ' Return the new SecurityElement.
            Return se
        End Function

The FromXml method recreates the state of a RadioPermission object that was rendered previously to XML using the ToXml method. Check first to see if the XML represents an unrestricted permission; if it does not, look for each individual action:

# C#

        // Extract state from a SecurityElement.
        public override void FromXml(SecurityElement e) {
        
            // Ensure we have a SecurityElement to work with
            if (e == null) throw new ArgumentNullException("e");
            
            // Ensure the SecurityElement is an IPermission
            if (e.Tag != "IPermission") {throw new ArgumentException(
                "Element must be IPermission");
            } else {

                // Determine if the permission is unrestricted    
                if (e.Attribute("Unrestricted") == "true") {  

                    RadioActions = RadioAction.All;    
                } else {
        
                    // Look for each individual action attribute to 
                    // build the set of permissions
                    RadioActions = RadioAction.None;

                    if (e.Attribute("StartStop") == "true") {      
                        RadioActions = 
                            RadioActions | RadioAction.StartStop;     
                    }
                    if (e.Attribute("Channel") == "true") {    
                        RadioActions = 
                            RadioActions | RadioAction.Channel;  
                    }
                    if (e.Attribute("Volume") == "true") {  
                        RadioActions = 
                            RadioActions | RadioAction.Volume;  
                    }
                }
            }
        }  
    }


# Visual Basic .NET

        ' Extract state from a SecurityElement.
        Public Overrides  Sub FromXml(ByVal e As SecurityElement)
 
            ' Ensure we have a SecurityElement to work with
            If e Is Nothing Then
                Throw New ArgumentNullException("e")
            End If
 
            ' Ensure the SecurityElement is an IPermission
            If e.Tag <> "IPermission" Then
                Throw New ArgumentException("Element must be IPermission")
            Else 
 
                ' Determine if the permission is unrestricted    
                If e.Attribute("Unrestricted") = "true" Then
                    RadioActions = RadioAction.All
                Else 
                    ' Look for each individual action attribute to 
                    ' build the set of permissions
                    RadioActions = RadioAction.None
 
                    If e.Attribute("StartStop") = "true" Then
                        RadioActions = _
                            RadioActions Or RadioAction.StartStop
                    End If
                    If e.Attribute("Channel") = "true" Then
                        RadioActions = _
                            RadioActions Or RadioAction.Channel
                    End If
                    If e.Attribute("Volume") = "true" Then
                        RadioActions = _
                            RadioActions Or RadioAction.Volume
                    End If
                End If
            End If
        End Sub
    End Class
7.3.1.8 Defining the RadioPermissionAttribute class

We begin the declaration of the RadioPermissionAttribute class by using the AttributeUsage attribute to define the program elements you can apply the RadioPermissionAttribute to.

RadioPermissionAttribute extends the CodeAccessSecurityAttibute class, which provides the base class from which all permission attributes must extend. Implement a single constructor that all permission attributes extending CodeAccessSecurityAttibute must define. The constructor takes a value from the SecurityAction enumeration, which describes the CAS operation invoked by a declarative security statement; see Section 7.2.1.2 for details:

# C#

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor 
    | AttributeTargets.Class | AttributeTargets.Struct 
    | AttributeTargets.Assembly, AllowMultiple = true, 
    Inherited = false )]
    [Serializable]
    public sealed class RadioPermissionAttribute : 
        CodeAccessSecurityAttribute {

        // Constructor to take SecurityAction, simply calls base 
        // class constructor.
        public RadioPermissionAttribute(SecurityAction action): 
            base( action ) {}

# Visual Basic .NET

    <AttributeUsage(AttributeTargets.Method Or _
    AttributeTargets.Constructor Or AttributeTargets.Class Or _
    AttributeTargets.Struct Or AttributeTargets.Assembly, _
    AllowMultiple := True, Inherited := False), Serializable> _ 
    Public NotInheritable Class RadioPermissionAttribute
         Inherits CodeAccessSecurityAttribute
 
        ' Constructor to take SecurityAction, simply calls base 
        ' class constructor.
        Public Sub New(ByVal action As SecurityAction)
            MyBase.New(action) 
        End Sub

As with RadioPermission, the RadioPermissionAttribute class contains a private data member named RadioActions (of type RadioAction) to identify which actions the RadioPermissionAttribute object represents. Four properties provide controlled access to configure the value of the private RadioActions member. The first property, named Actions, allows you to provide a RadioAction value to configure the state of all actions in a single call. The other three attributes, named StartStop, Channel, and Volume, allow you to configure the individual bits of RadioActions to set whether an individual action is permitted or not:

# C#

        // Private member to signal permitted actions.
        private RadioAction RadioActions = RadioAction.None;
        
        // Property allows all actions to be configured in one call
        // using values from the RadioAction enumeration
        public RadioAction Actions {
            get { return RadioActions; }
            set { RadioActions = value; }
        }

        // Property to allow StartStop permission to be switched on
        // and off                
        public bool StartStop {
            get { return (RadioActions & RadioAction.StartStop) != 0; }
            set { 
                if (value) {
                    RadioActions = RadioActions | RadioAction.StartStop;
                } else {
                    RadioActions = RadioActions & ~RadioAction.StartStop;
                }
            }
        }
        
        // Property to allow Channel permission to be switched on
        // and off                
        public bool Channel {
            get { return (RadioActions & RadioAction.Channel) != 0; }
            set { 
                if (value) {
                    RadioActions = RadioActions | RadioAction.Channel;
                } else {
                    RadioActions = RadioActions & ~RadioAction.Channel;
                }
            }
        }

        // Property to allow Volume permission to be switched on
        // and off                
        public bool Volume {
            get { return (RadioActions & RadioAction.Volume) != 0; }
            set { 
                if (value) {
                    RadioActions = RadioActions | RadioAction.Volume;
                } else {
                    RadioActions = RadioActions & ~RadioAction.Volume;
                }
            }
        }


# Visual Basic .NET

        ' Private member to signal permitted actions.
        Private RadioActions As RadioAction =  RadioAction.None 
 
        ' Property allows all actions to be configured in one call
        ' using values from the RadioAction enumeration
        Public Property Actions(  ) As RadioAction
            Get 
                Return RadioActions
            End Get
            Set (ByVal Value As RadioAction) 
                 RadioActions = value
            End Set
        End Property
 
        ' Property to allow StartStop permission to be switched on
        ' and off               
        Public Property StartStop(  ) As Boolean
            Get 
                Return (RadioActions And RadioAction.StartStop) <> 0
            End Get
            Set (ByVal Value As Boolean) 
                If value Then
                    RadioActions = RadioActions Or RadioAction.StartStop
                Else 
                    RadioActions = RadioActions And _
                    (Not RadioAction.StartStop)
                End If
            End Set
        End Property
 
        ' Property to allow Channel permission to be switched on
        ' and off               
        Public Property Channel(  ) As Boolean
            Get 
                Return (RadioActions And RadioAction.Channel) <> 0
            End Get
            Set (ByVal Value As Boolean) 
                If value Then
                    RadioActions = RadioActions Or RadioAction.Channel
                Else 
                    RadioActions = RadioActions And _
                    (Not RadioAction.Channel)
                End If
            End Set
        End Property
 
        ' Property to allow Volume permission to be switched on
        ' and off                
        Public Property Volume(  ) As Boolean
            Get 
                Return (RadioActions And RadioAction.Volume) <> 0
            End Get
            Set (ByVal Value As Boolean) 
                If value Then
                    RadioActions = RadioActions Or RadioAction.Volume
                Else 
                    RadioActions = RadioActions And _
                    (Not RadioAction.Volume)
                End IF
            End Set
        End Property

Despite its simplicity, the CreatePermission method is the most important method of a permission attribute. It allows the runtime to create permission objects from the declarative security statements contained in your code. The CreatePermission method returns a newly created RadioPermission using the value of attributes RadioActions member:

# C#

        // Creates and returns an RadioPermission object
        // based on the configured radio actions.
        public override IPermission CreatePermission(  ) {
       
            return new RadioPermission(RadioActions);
        }
    }    
}

# Visual Basic .NET

        ' Creates and returns an RadioPermission object
        ' based on the configured radio actions.
        Public Overrides Function CreatePermission(  ) As IPermission
            Return New RadioPermission(RadioActions)
        End Function
    End Class   
End Namespace
7.3.1.9 Building the Radio.dll library

The RadioAction, RadioPermission, and RadioPermissionAttribute classes are all contained in a single source file named Radio.cs (C#) or Radio.vb (Visual Basic .NET). Before you can build the Radio.dll library containing the new security classes, you must create a key file named Keys.snk to satisfy the reference contained in the AssemblyKeyFile attribute. Create the Keys.snk file and compile the Radio.dll library using the following commands:

# C# 

sn -k Keys.snk
csc /target:library Radio.cs

# Visual Basic .NET

sn -k Keys.snk
vbc /target:library Radio.vb
7.3.1.10 Using RadioPermission to enforce security

RadioPermission is now ready to protect the methods in the managed library that provides access to the radio tuner. As demonstrated in the following code fragments, you can use RadioPermission in exactly the same ways as the standard permission classes:

# C#

// An imperative assert of RadioPermission granting Volume control
RadioPermission r = new RadioPermission(RadioAction.Volume);
r.Assert(  );

// A declarative demand for permission to change the radio channel
[RadioPermission(SecurityAction.Demand, Channel = true)]

// A minimum permission request for unrestricted access to the radio
[assembly:RadioPermission(SecurityAction.RequestMinimum, 
Unrestricted = true)]

# Visual Basic .NET

' An imperative assert of RadioPermission granting Volume control
Dim r As RadioPermission = New RadioPermission(RadioAction.Volume)
r.Assert(  )

' A declarative demand for permission to change the radio channel
<RadioPermission(SecurityAction.Demand, Channel := True)> _

' A minimum permission request for unrestricted access to the radio
<assembly:RadioPermission(SecurityAction.RequestMinimum, _
Unrestricted := True)>

Once you have developed the secure class library that enables managed code to control the PC radio tuner, you must configure the security policy to grant access to the appropriate code. First, you must understand how security policy calculates the set of permissions to grant to an assembly. Then you need to understand how to use the security administration tools supplied with the .NET Framework to configure security policy. We discuss security policy in Chapter 8 and the .NET administration tools in Chapter 9.

    [ Team LiB ] Previous Section Next Section