воскресенье, 24 октября 2010 г.

Protect ASP.NET application using name of the domain specified in the HTTP request.

Recently I’ve answered several times the question about protection of the ASP.NET application using name of the domain specified in the HTTP request. Since this licensing schema isn’t described in our 200-pages documentation yet I’ve decided to describe it in my blog. I suppose that you've read already “Quick start using ‘Unlock Key’ licensing schema” and “Protect ASP.NET application using ‘Unlock Key with Activation’ licensing schema” topics in our product documentation, so I will describe only things are specific for this licensing schema.
The first major difference is the way how you deliver licensing information to the customer. In this case you provide customer with license file instead of Unlock Key.
The next difference between simple “Unlock Key” and “HTTP request domain” schemas is a number of the license types which are necessary to fully implement schema. The “Unlock Key” requires just 1 license type, the “HTTP request domain” require 2 license types:

The “Evaluation” license type is intended to provide your customers with trial version of your product. It could locks like the following:



Expiration Days – this rule sets number of days during which your product will work in evaluation mode. This license rule can be used to create time-limited evaluation licenses. When someone attempts use licensed product after given number of days it will give “Your license has been expired” exception.
Is Evaluation – this rule indicates, that license is in evaluation mode. Allows you limit some functionality in your product using evaluation status of license.
You have to create only one license file of this type. You even can distribute this license file with your application.
The “Deployment” license type is intended to provide your customers with fully functional version of your product.  We don’t include any license rule to this license type. It will use ability of the protection library validate license using custom values:


We added 1 custom value “AllowedDomains” into our license type. This custom value will contain comma-separated list of the HTTP domains are allowed.
Protection library can compare custom value with one is available in your application as strings (by default) or use specific class class-comparer for this purposes. This class must implement IComparer<string> (IComparer(of String)) interface. Since we suppose that “AllowedDomains” custom value can contain list of the domains, then we should create own class-comparer which will walk through the list.
Add new RequestDomainComparer class into your project:
[C#]
/// <summary>
/// Compare Domain Name from the HTTP request.
/// </summary>
public class RequestDomainComparer : IComparer<string>
{
       private Manco.Licensing.License license;

       /// <summary>
       /// Creates instance of the RequestDomainComparer class.
       /// </summary>
       /// <param name="license">License object.</param>
       public RequestDomainComparer(Manco.Licensing.License license)
       {
             this.license = license;
       }
            
       /// <summary>
       /// Compare value set from application with value from
       /// the license file.
       /// </summary>
       /// <param name="valueFromApplication">Value set from application:
       /// domain name from the HTTP request.</param>
       /// <param name="customValueFromLicenseFile">Custom value set in
       /// the license file.</param>
       /// <returns>0 if domain rom HTTP request is in the list
       /// of the allowed domains. -1 otherwise.</returns>
       public int Compare(
              string valueFromApplication,
              string customValueFromLicenseFile)
       {
              if (license.IsEvaluation)
              {
                    // During evaluation time we
                    // allow only localhost domain.
                    return valueFromApplication
                           .Trim()
                           .ToLowerInvariant()
                           .CompareTo("localhost");
              }
              else
              {
                    int retVal = -1;
                    string domainFromRequest = valueFromApplication
                           .Trim()
                           .ToLowerInvariant();

                    if (domainFromRequest == "localhost")
                    {
                           // "localhost" domain name is always allowed.
                           retVal = 0;
                    }
                    else
                    {
                           // We suppose that custom value in the
                           // license file can contain list of
                           // the domains separated by comma.
                           string[] domainList =
                                  customValueFromLicenseFile.Split(',');
                           foreach (string allowedDomain in domainList)
                           {
                                  if (allowedDomain
                                         .Trim()
                                         .ToLowerInvariant()
                                         == domainFromRequest)
                                  {
                                         // We found allowed domain.
                                         // Comparision should return 0.
                                         retVal = 0;
                                         break;
                                  }
                           }
                    }

                    return retVal;
              }
       }
}

[VB.NET]
''' <summary>
''' Compare Domain Name from the HTTP request.
''' </summary>
Public Class RequestDomainComparer
       Implements IComparer(Of String)

       Private license As Manco.Licensing.License

       ''' <summary>
       ''' Creates instance of the RequestDomainComparer class.
       ''' </summary>
       ''' <param name="license">License object.</param>
       Public Sub New(ByVal license As Manco.Licensing.License)
             Me.license = license
       End Sub

       ''' <summary>
       ''' Compare value set from application with value from
       ''' the license file.
       ''' </summary>
       ''' <param name="valueFromApplication">Value set from
       ''' application: domain name from the HTTP request.</param>
       ''' <param name="customValueFromLicenseFile">Custom value set
       ''' in the license file.</param>
       ''' <returns>0 if domain rom HTTP request is in the list of
       ''' the allowed domains. -1 otherwise.</returns>
       Public Function Compare( _
         ByVal valueFromApplication As String, _
         ByVal customValueFromLicenseFile As String) _
         As Integer Implements IComparer(Of String).Compare

             If license.IsEvaluation Then
                    ' During evaluation time we allow
                    ' only localhost domain.
                    Return valueFromApplication _
                    .Trim() _
                    .ToLowerInvariant() _
                    .CompareTo("localhost")
             Else
                    Dim retVal As Integer = -1
                    Dim domainFromRequest As String = _
                    valueFromApplication _
                    .Trim() _
                    .ToLowerInvariant()

                    If domainFromRequest = "localhost" Then
                           ' "localhost" domain name is always allowed.
                           retVal = 0
                    Else
                           ' We suppose that custom value in the
                           ' license file can contain list of
                           ' the domains separated by comma.
                           Dim domainList() As String = _
                            customValueFromLicenseFile.Split(",")
                           For Each allowedDomain As String In domainList
                            If allowedDomain _
                             .Trim() _
                             .ToLowerInvariant() _
                             = domainFromRequest Then
                             ' We found allowed domain.
                             ' Comparision should return 0.
                             retVal = 0
                             Exit For
                            End If
                           Next
                    End If

                    Return retVal
             End If
       End Function
End Class

You should extend your protected class (class which implements “ILicenseKeyProvider” interface and contains static license object) with comparer static (shared) variable and new method which will pass name of the domain to compare from your application to the license object:

[C#]
[LicenseProviderAttribute(typeof(Manco.Licensing.LicenseProvider))]
public class MyAppLicense : ILicenseKeyProvider
{
       /// <summary>
       /// License object
       /// </summary>
       private static Manco.Licensing.License license = null;

       /// <summary>
       /// Comparer which will be used to validate
       /// domain in the HTTP request.
       /// </summary>
       private static RequestDomainComparer comparer = null;

       /// <summary>
       /// Constructor
       /// </summary>
       public MyAppLicense()
       {
              if (license == null)
              {
                    try
                    {
                           license =
                                  (Manco.Licensing.License)LicenseManager
                                  .Validate(typeof(MyAppLicense), this);
                           license.CallingAssembly =
                                  this.GetType().Assembly;
                           license.LicensedAssembly =
                                  this.GetType().Assembly;

                           comparer = new RequestDomainComparer(license);
                    }
                    catch (FileNotFoundException ex)
                           {
                           // License file not found
                           throw
                           new Exception(
                           String.Format("License file was not found!",
                           ex.FileName));
                    }
              }
       }


       /// <summary>
       /// Specify name of the custom value which
       /// will be used to get list of the
       /// allowed domains and set value to
       /// search in list (domain name from
       /// the HTTP request).
       /// </summary>
       /// <param name="name">Name of the custom value
       /// which will be used to get list of the allowed domains.</param>
       /// <param name="value">Domain name from the HTTP request.</param>
       public void SetCustomValueToValidate(string name, string value)
       {
             license.AddCustomValueToValidate(name, value, comparer);
       }
      
      
}

[VB.NET]
<LicenseProvider(GetType(Manco.Licensing.LicenseProvider))> _
Public Class MyAppLicense
       Implements ILicenseKeyProvider

       ''' <summary>
       ''' License object
       ''' </summary>
       Private Shared license As Manco.Licensing.License = Nothing

       ''' <summary>
       ''' Comparer which will be used to validate
       ''' domain in the HTTP request.
       ''' </summary>
       Private Shared comparer As RequestDomainComparer = Nothing

       ''' <summary>
       ''' Constructor
       ''' </summary>
       Public Sub New()
             If license Is Nothing Then
                    Try
                           license = _
                           LicenseManager.Validate(GetType(MyAppLicense), _
                           Me)
                           license.CallingAssembly = _
                           System.Reflection.Assembly.GetCallingAssembly()
                           license.LicensedAssembly = _
                           Me.GetType().Assembly

                           comparer = New RequestDomainComparer(license)
                    Catch ex As IO.FileNotFoundException
                           ' License file not found
                           Throw _
                           New Exception( _
                           String.Format("License file was not found!", _
                           ex.FileName))
                    End Try
             End If
       End Sub

      

       ''' <summary>
       ''' Specify name of the custom value
       ''' which will be used to get list of the
       ''' allowed domains and set value to
       ''' search in list (domain name from
       ''' the HTTP request).
       ''' </summary>
       ''' <param name="name">Name of the custom value
       ''' which will be used to get list of the allowed domains.</param>
       ''' <param name="value">Domain name from the HTTP request.</param>
       Public Sub SetCustomValueToValidate( _
       ByVal name As String, _
       ByVal value As String)
             license.AddCustomValueToValidate(name, value, comparer)
       End Sub

      

End Class

Depends on your needs you can validate license on per-session or on per-page basis. The validation code could looks like the following:


[C#]
string hostName = HttpContext.Current.Request.Url.DnsSafeHost;
m_oLicense.SetCustomValueToValidate("AllowedDomains", hostName);

// Check that license is valid and not expired.
if (m_oLicense.IsExpired || !m_oLicense.IsValid)
{
       // If license have been expired or it is not valid
       // then we shouldn't allow session start.
       // Throw exception.
       throw new Exception("Illegal use of this application: no valid License found");
}

[VB.NET]
Dim hostName As String = HttpContext.Current.Request.Url.DnsSafeHost
m_oLicense.SetCustomValueToValidate("AllowedDomains", hostName)

' Check that license is valid and not expired.
If m_oLicense.IsExpired Or Not m_oLicense.IsValid Then
       ' If license have been expired or it is not valid
       ' then we shouldn't allow session start.
       ' Throw exception.
       Throw New Exception("Illegal use of this application: no valid License found")
End If

When you create deployment licenses for your customers you should specify list of the HTTP domains are allowed in the request URL. For example: 




You can find sample solutions which implements this licensing schema on our official site at the http://www.mancosoftware.com/licensing/download.htm