Partager via


Implementing a Custom Permission

All permission objects must implement the IPermission interface. Inheriting from the CodeAccessPermission class is the easiest way to create a custom permission because CodeAccessPermission implements IPermission and provides most of the methods required for a permission. Additionally, you must implement the IUnrestrictedPermision interface for all custom code access permissions. The custom permission class is required for both imperative and declarative security support, so you should create it even if you plan to use only declarative security.

Defining the Permission Class

To derive from the CodeAccessPermission class, you must override the following five key methods and provide your own implementation:

  • Copy creates a duplicate of the current permission object.
  • Intersect returns the intersection of allowed permissions of the current class and a passed class.
  • IsSubsetOf returns true if a passed permission includes everything allowed by the current permission.
  • FromXml decodes an XML representation of your custom permission.
  • ToXml encodes an XML representation of your custom permission.

The IUnrestrictedPermission interface requires you to override and implement a single method called IsUnrestrictedPermission. In order to support the IUnrestrictedPermission interface, you must implement some system, such as a Boolean value that represents the state of restriction in the current object, to define whether the current instance of the permission is unrestricted.

The following code fragment illustrates the manner in which a custom permission class might be defined. A constructor that accepts a PermissionState enumeration and a Boolean value called unrestricted are both created. The PermissionState enumeration has a value of either Unrestricted or None. If the passed enumeration has a value of Unrestricted, the constructor sets unrestricted to true. Otherwise, unrestricted is set to false. In addition to constructors specific to your custom permission, all code access permissions (any permission that inherits from CodeAccessPermission) must support a constructor that takes only a PermissionState enumeration.

In addition to the code shown in the following example, you must implement IsUnrestricted method and override the Copy, Intersect, IsSubsetOf, ToXML, and FromXML methods. For information about completing these steps, see the sections that follow the example.

Option Strict
Option Explicit
Imports System
Imports System.Security
Imports System.Security.Permissions
<SerializableAttribute()> NotInheritable Public Class CustomPermission
   Inherits CodeAccessPermission
   Implements IUnrestrictedPermission
   Private unrestricted As Boolean
   
   
   Public Sub New(state As PermissionState)
      If state = PermissionState.Unrestricted Then
         unrestricted = True
      Else
         unrestricted = False
      End If
   End Sub
   'Define the rest of your custom permission here. You must 
   'implement IsUnrestricted and override the Copy, Intersect, 
   'IsSubsetOf, ToXML, and FromXML methods.
End Class
[C#]
using System;
using System.Security;
using System.Security.Permissions;

[SerializableAttribute()]
public sealed class CustomPermission: CodeAccessPermission, IUnrestrictedPermission
{  
   private bool unrestricted;

   public CustomPermission(PermissionState state)
   {
      if(state == PermissionState.Unrestricted)
      {
         unrestricted = true;
      }
      else
      {
         unrestricted = false;
      }
   }     

   //Define the rest of your custom permission here. You must 
   //implement IsUnrestricted and override the Copy, Intersect, 
   //IsSubsetOf, ToXML, and FromXML methods.
}

Notice that the class is marked with SerializableAttribute. You must mark your class with SerializableAttribute in order to support declarative syntax using an attribute. For information about creating a custom attribute that uses a custom security object, see Adding Declarative Security Support.

Implementing the IsUnrestricted Method

The IsUnrestricted method is required by the IUnrestrictedPermission interface and simply returns a Boolean value that indicates whether the current instance of the permission has unrestricted access to the resource protected by the permission. To implement this method, simply return the value of unrestricted.

The following code example implements the IsUnrestricted method.

Public Function IsUnrestricted() As Boolean Implements IUnrestrictedPermission.IsUnrestricted
   Return unrestricted
End Function
[C#]
public bool IsUnrestricted()
{
   return unrestricted;
}

Overriding the Copy Method

The copy method is required by the CodeAccessPermission class and returns a copy of the current permission class.

The following code illustrates how to override the Copy method.

Public Overrides Function Copy() As IPermission
   Dim myCopy As New CustomPermission(PermissionState.None)
   
   If Me.IsUnrestricted() Then
      myCopy.unrestricted = True
   Else
      myCopy.unrestricted = False
   End If
   Return myCopy
End Function
[C#]
public override IPermission Copy()
{
   CustomPermission copy = new CustomPermission(PermissionState.None);

   if(this.IsUnrestricted())
   {
      copy.unrestricted = true;
   }
   else
   {
      copy.unrestricted = false;
   }
   return copy;
} 

Overriding the Intersect and IsSubsetOf Methods

All permissions must implement the Intersect and IsSubsetOf methods. The behavior of these operations must be implemented as follows:

  • X.IsSubsetOf(Y) is true if permission Y includes everything allowed by X.
  • X.Intersect(Y) results in a permission that allows all operations and only those operations allowed by both the X and Y permissions.

The following example illustrates how to override and implement the Intersect method. The method accepts a class that derives from IPermission and initializes this class to a new instance of the CustomPermisison object. In this case, the intersection of the current object and the passed object is a final object with the value of unrestricted if both objects have that value. However, if one of the two objects has a false value for unrestricted, then the final object will also have a false value for unrestricted. This code returns an unrestricted object only if both objects are unrestricted.

Public Overrides Function Intersect(target As IPermission) As IPermission
   If Nothing Is target Then
      Return Nothing
   End If
   Try
      Dim PassedPermission As CustomPermission = CType(target, CustomPermission)
      If Not PassedPermission.IsUnrestricted() Then
         Return PassedPermission
      End If
      Return Me.Copy()
   Catch InvalidCastException As Exception
      Throw New ArgumentException("Argument_WrongType", Me.GetType().FullName)
      End Try
End Function
[C#]

public override IPermission Intersect(IPermission target)
{
   try
   {
      if(null == target)
      {
         return null;
      }
      CustomPermission PassedPermission = (CustomPermission)target;

      if(!PassedPermission.IsUnrestricted())
      {
         return PassedPermission;
      }
      return this.Copy();
   }
   catch (InvalidCastException)
   {
      throw new ArgumentException("Argument_WrongType", this.GetType().FullName);
   }                
}

In the following example, the IsSubsetOf method is overridden. In order for this method to return true, the current instance and a passed instance must allow exactly the same set of operations. In this case, the overridden method initializes a new instance of the CustomPermission object to the passed permission object. If the unrestricted values are the same, then the method returns true. If they are not, the method returns false.

Public Overrides Function IsSubsetOf(target As IPermission) As Boolean
   If Nothing Is target Then
      Return Not Me.unrestricted
   End If
   Try
      Dim passedpermission As CustomPermission = CType(target, CustomPermission)
      If Me.unrestricted = passedpermission.unrestricted Then
         Return True
      Else
         Return False
      End If
   Catch InvalidCastException As Exception
      Throw New ArgumentException("Argument_WrongType", Me.GetType().FullName)
      End Try
End Function
[C#]
public override bool IsSubsetOf(IPermission target)
{  
   if(null == target)
   {
      return !this.unrestricted;
   }
   try
   {        
      CustomPermission passedpermission = (CustomPermission)target;
      if(this.unrestricted == passedpermission.unrestricted)
      {
         return true;
      }
      else
      {
         return false;
      }
   }
   catch (InvalidCastException)
   {
      throw new ArgumentException("Argument_WrongType", this.GetType().FullName);
   }                
}

Overriding the ToXml and FromXml Methods

Permissions support XML encoding so that a permission object can be saved as XML and then another permission object can be restored to the value of the original, using the XML file. To support XML encoding, your custom permission must implement the ISecurityEncodable interface, which defines a ToXml and a FromXml method. Because both methods are implemented by CodeAccessPermission, if your custom permission class derives from CodeAccessPermission, you should override these methods.

The content of the XML element that represents the object state is determined by the object itself. The FromXML method can use any XML representation as long as ToXML can interpret it and restore the same state. However, the containing Permission element must be of a standard form. For example, the form for CustomPermission might look like the following:

<IPermission class="CustomPermissions.CustomPermission, CustomPermissionAssembly " version="1" Unrestricted="True">

The IPermission element contains three attributes:

  • class: Contains the type name disambiguated by the name of the assembly that contains it.
  • version: Specifies the version of the XML encoding (not the version of the class's assembly).
  • Unrestricted: Specifies whether the permission has unrestricted rights.

All permissions must be encoded in an XML element called IPermission in order to be used by the common language runtime security system.

New versions of a permission object should remain backward compatible with information persisted in XML from previous versions. The version tag provides information to a permission object about which version originally encoded the data.

The SecurityElement class encapsulates the main functionality that you need in order to create and interact with XML-encoded permission objects. However, because the XML object model used for .NET Framework security is different from other XML object models, the SecurityElement class should not be used to generate other types of XML files. See the description of the SecurityElement class for a complete list of its members.

The following code fragment creates an XML Permission element:

Public Overrides Function ToXml() As SecurityElement
   Dim element As New SecurityElement("IPermission")
   Dim type As Type = Me.GetType()
   Dim AssemblyName As New StringBuilder(type.Assembly.ToString())
   AssemblyName.Replace(ControlChars.Quote, "'"c)
   element.AddAttribute("class", type.FullName & ", " & AssemblyName.ToString)
   element.AddAttribute("version", "1")
   element.AddAttribute("Unrestricted", unrestricted.ToString())
   Return element
End Function
[C#]
public override SecurityElement ToXml()
{
   SecurityElement element = new SecurityElement("IPermission");
   Type type = this.GetType();
   StringBuilder AssemblyName = new StringBuilder(type.Assembly.ToString());
   AssemblyName.Replace('\"', '\'');
   element.AddAttribute("class", type.FullName + ", " + AssemblyName);
   element.AddAttribute("version", "1");
   element.AddAttribute("Unrestricted", unrestricted.ToString());
   return element;
}

Notice that the previous example uses the StringBuilder.Replace method. Attributes in the SecurityElement class cannot contain double quotes, but some assembly name information is in double quotes. To handle this situation, the Replace method converts double quotes (") in the assembly name to single quotes (').

The following method reads a SecurityElement object created by the previous method and sets the current value of the Unrestricted property to the one specified by the passed object. This method should ensure that any information stored by the ToXml method is retrieved.

Public Overrides Sub FromXml(PassedElement As SecurityElement)
   Dim element As String = PassedElement.Attribute("Unrestricted")
   If Not element Is Nothing Then
      Me.unrestricted = Convert.ToBoolean(element)
   End If
End Sub
[C#]
public override void FromXml(SecurityElement PassedElement)
{
   string element = PassedElement.Attribute("Unrestricted");
   if(null != element)
   {  
      this.unrestricted = Convert.ToBoolean(element);
   }
}

Custom Permission Example

The following code example shows an entire custom permission class:

Option Explicit
Option Strict
Imports System
Imports System.Text
Imports System.Security
Imports System.Security.Permissions
Imports Microsoft.VisualBasic

<Serializable()>NotInheritable Public Class CustomPermission
   Inherits CodeAccessPermission
   Implements IUnrestrictedPermission
   Private unrestricted As Boolean
   Public Sub New(state As PermissionState)
      If state = PermissionState.Unrestricted Then
         unrestricted = True
      Else
         unrestricted = False
      End If
   End Sub

   Public Function IsUnrestricted() As Boolean Implements IUnrestrictedPermission.IsUnrestricted
      Return unrestricted
   End Function

   Public Overrides Function Copy() As IPermission
      'Create a new instance of CustomPermission with the current
      'value of unrestricted.
      Dim myCopy As New CustomPermission(PermissionState.None)
      
      If Me.IsUnrestricted() Then
         myCopy.unrestricted = True
      Else
         myCopy.unrestricted = False
      End If
      'Return the copy.
      Return copy
   End Function

   Public Overrides Function Intersect(target As IPermission) As IPermission
      'If nothing was passed, return null.
      If Nothing Is target Then
         Return Nothing
      End If
      Try
         'Create a new instance of CustomPermission from the passed object.
         Dim PassedPermission As CustomPermission = CType(target, CustomPermission)
         'If one class has an unrestricted value of false, then the
         'intersection will have an unrestricted value of false.
         'Return the passed class with the unrestricted value of false.
         If Not PassedPermission.unrestricted Then
            Return target
         End If
         'Return a copy of the current class if the passed one has
         'an unrestricted value of true.
         Return Me.Copy()

      'Catch an InvalidCastException.
      'Throw ArgumentException to notify the user.
      Catch InvalidCastException As Exception
         Throw New ArgumentException("Argument_WrongType", Me.GetType().FullName)
      End Try

   End Function

   Public Overrides Function IsSubsetOf(target As IPermission) As Boolean
      'If nothing was passed and unrestricted is false,
      ' return true. 
 
      If Nothing Is target Then
         Return Not Me.unrestricted
      End If
      Try
         'Create a new instance of CustomPermission from the passed object.
         Dim passedpermission As CustomPermission = CType(target, CustomPermission)
         'If unrestricted has the same value in both objects, then
         'one is the subset of the other.
         If Me.unrestricted = passedpermission.unrestricted Then
            Return True
         Else
            Return False
         End If

      'Catch an InvalidCastException.
      'Throw ArgumentException to notify the user.
      Catch InvalidCastException As Exception
         Throw New ArgumentException("Argument_WrongType", Me.GetType().FullName)
      End Try

   End Function
   
   
   Public Overrides Sub FromXml(PassedElement As SecurityElement)
      'Get the unrestricted value from the XML and initialize 
      'the current instance of unrestricted to that value.
      Dim element As String = PassedElement.Attribute("Unrestricted")
      If Not element Is Nothing Then
         Me.unrestricted = Convert.ToBoolean(element)
   End If
   End Sub
   
   
   Public Overrides Function ToXml() As SecurityElement
      'Encode the current permission to XML using the 
      'SecurityElement class.
      Dim element As New SecurityElement("IPermission")
      Dim type As Type = Me.GetType()
      Dim AssemblyName As New StringBuilder(type.Assembly.ToString())
      AssemblyName.Replace(ControlChars.Quote, "'"c)
      element.AddAttribute("class", type.FullName & ", " & AssemblyName.ToString)
      element.AddAttribute("version", "1")
      element.AddAttribute("Unrestricted", unrestricted.ToString())
      Return element
   End Function
End Class
[C#]
using System;
using System.Text;
using System.Security;
using System.Security.Permissions;

[Serializable()]
public sealed class CustomPermission: CodeAccessPermission , IUnrestrictedPermission
{
   private bool unrestricted;

   public CustomPermission(PermissionState state)
   {
      if(state == PermissionState.Unrestricted)
      {
         unrestricted = true;
      }
      else
      {
         unrestricted = false;
      }
   }
      
   public bool IsUnrestricted()
   {
      return unrestricted;
   }

   public override IPermission Copy()
   {
      //Create a new instance of CustomPermission with the current
      //value of unrestricted.
      CustomPermission copy = new CustomPermission(PermissionState.None);

      if(this.IsUnrestricted())
      {
         copy.unrestricted = true;
      }
      else
      {
         copy.unrestricted = false;
      }
      //Return the copy.
      return copy;
   }

   public override IPermission Intersect(IPermission target)
   {
      //If nothing was passed, return null.
      if(null == target)
      {
         return null;
      }
      try
      {
         //Create a new instance of CustomPermission from the passed object.
         CustomPermission PassedPermission = (CustomPermission)target;

         //If one class has an unrestricted value of false, then the
         //intersection will have an unrestricted value of false.
         //Return the passed class with the unrestricted value of false.
         if(!PassedPermission.unrestricted)
         {
            return target;
         }
         //Return a copy of the current class if the passed one has
         //an unrestricted value of true.
         return this.Copy();
      }
      //Catch an InvalidCastException.
      //Throw ArgumentException to notify the user.
      catch (InvalidCastException)
      {
         throw new ArgumentException("Argument_WrongType", this.GetType().FullName);
      }                
   }

   public override bool IsSubsetOf(IPermission target)
   {
      //If nothing was passed and unrestricted is false,
      //then return true. 
      if(null == target)
      {
         return !this.unrestricted;
      }
       try
      {        
         //Create a new instance of CustomPermission from the passed object.
         CustomPermission passedpermission = (CustomPermission)target;

         //If unrestricted has the same value in both objects, then
         //one is the subset of the other.
         if(this.unrestricted == passedpermission.unrestricted)
         {
            return true;
         }
         else
         {
            return false;
         } 
      }
      //Catch an InvalidCastException.
      //Throw ArgumentException to notify the user.
      catch (InvalidCastException)
      {
         throw new ArgumentException("Argument_WrongType", this.GetType().FullName);
      }                    
   }

   public override void FromXml(SecurityElement PassedElement)
   {
      //Get the unrestricted value from the XML and initialize 
      //the current instance of unrestricted to that value.
      string element = PassedElement.Attribute("Unrestricted");         
 
      if(null != element)
      {  
         this.unrestricted = Convert.ToBoolean(element);
      }
   }

   public override SecurityElement ToXml()
   {
      //Encode the current permission to XML using the 
      //SecurityElement class.
      SecurityElement element = new SecurityElement("IPermission");
      Type type = this.GetType();
      StringBuilder AssemblyName = new StringBuilder(type.Assembly.ToString());
      AssemblyName.Replace('\"', '\'');
      element.AddAttribute("class", type.FullName + ", " + AssemblyName);
      element.AddAttribute("version", "1");
      element.AddAttribute("Unrestricted", unrestricted.ToString());
      return element;
   }
}

See Also

Creating Your Own Code Access Permissions | Code Access Security | IPermission Interface | CodeAccessPermission Class | IUnrestrictedPermission Interface | SerializableAttribute Class | Adding Declarative Security Support | ISecurityEncodable Interface | SecurityElement Class