Licensing


A license is a policy or agreement between you and the page developer that describes the legal usage of your controls. Licensing enables you to enforce that policy to determine how page developers use your controls and to authorize who can use your controls. Licensing also allows you to enable and disable certain features based on the type of license granted to the developer. Licensing is particularly applicable to commercial control development.

The .NET Framework defines an extensible and common licensing framework for use by all components. The licensing framework isn't designed to be completely tamper-proof. Rather, the licensing framework makes using unlicensed components relatively harder and makes it obvious when a developer is violating the licensing policy. Using the licensing framework, you can incorporate custom and arbitrarily complex licensing schemes for your component, such as the following:

  • A simple scheme that only checks for the existence of valid license data to determine whether to enable your component.

  • A per-usage scheme in which the license expires after a certain date or usage count. This can be helpful in implementing demo versions of your component.

  • A scheme that enables the component only when the request comes from a specific client machine, such as the local machine. This can also be useful in implementing trial versions of your component.

  • A more complex validation scheme that requires page developers to register (and purchase) your component and receive a license that they can include in their applications.

The licensing framework revolves around the System.ComponentModel.LicenseProvider class, which you must implement to define a licensing scheme. You can then associate this license provider with your component by using the System.ComponentModel.LicenseProviderAttribute metadata attribute. Your component should incorporate license validation by invoking the IsValid or Validate methods of the LicenseManager class in its constructor. The LicenseManager inspects the component's metadata to select the appropriate LicenseProvider and to retrieve a validated License object from it. LicenseManager defines two usage modes: design time and run time. This allows a component to implement different licensing models for design-time usage and run-time usage. The samples in this section will demonstrate how these classes allow you to incorporate licensing into your server controls.

Listing 17-8 contains the implementation of a set of licensed controls associated with various licensing providers.

Listing 17-8 A set of licensed server controls using varying licensing schemes
 usingSystem; usingSystem.ComponentModel; usingSystem.Web.UI.WebControls; namespaceMSPress.ServerControls{ [LicenseProvider(typeof(ServerLicenseProvider))] publicclassLicensedLabel:Label{ publicLicensedLabel(){ LicenseManager.Validate(typeof(LicensedLabel)); } } [LicenseProvider(typeof(EncryptedLicenseProvider))] publicclassEncryptedLicensedLabel:Label{ publicEncryptedLicensedLabel(){ LicenseManager.Validate(typeof(EncryptedLicensedLabel)); } } [LicenseProvider(typeof(ExpiringLicenseProvider))] publicclassDemoLabel:Label{ publicDemoLabel(){ LicenseManager.Validate(typeof(DemoLabel)); } } } 

The samples in Listing 17-8 demonstrate the two additions you have to make to any control to incorporate licensing. First, you need to add metadata that specifies the type of license provider to use by applying the LicenseProviderAttribute attribute. For example, the DemoLabel control specifies that the ExpiringLicenseProvider class should be used when the control's license is validated. Next you need to invoke the Validate method of the LicenseManager and pass in your control's type as the argument. All usage of the control will now be subject to your licensing scheme. When a control's license is not found or is invalid, the license provider throws a System.ComponentModel.License ­Exception .

We will look at the specifics of the license providers used in the samples shown later in this section. Figure 17-3 shows an example of the error page that is generated when an unlicensed control is used on a page.

Figure 17-3. An example of the error generated when a page uses an unlicensed control

graphics/f17hn03.jpg

The licensing model employed by Visual Studio .NET collects license information from components used by a page developer at design time and embeds them as resources into an assembly at compile time, which can then be extracted and inspected for validity at run time. This model does not perfectly fit the needs of server controls and Web applications. When you implement licensing, you should be sure that the licensing scheme satisfies the following critical elements:

  • Supports a no-compile scenario

    ASP.NET Web applications use dynamic compilation and do not necessarily have a precompiled assembly associated with the application. Therefore, the licensing mechanism should not depend on finding licenses embedded as an assembly resource.

  • Focuses on run-time licensing

    Page developers use both visual design-time tools and simple text editors to develop their pages. Therefore, the licensing mechanism should focus on run-time validation rather than on design-time checking.

  • Supports xcopy deployment

    ASP.NET allows page developers to deploy their Web application by copying a folder across computers on their network and to have them continue to work as expected. Therefore, your licensing scheme should not depend on the registry and other machine-specific resources that do not work with simple xcopy deployment.

  • Supports a caching mechanism

    License data ideally should be retrieved only once per application, rather than on every page request, because the retrieval logic could involve expensive operations such as opening files and decrypting information. Instead, licenses should be created and cached the first time they are needed. You can still validate cached licenses each time they are used to implement usage-based licenses.

ServerLicenseProvider

In this section, we will use the extensibility of the licensing framework to implement ServerLicenseProvider , a custom license provider implementation. ServerLicenseProvider offers a framework for implementing licensing schemes optimized for server controls and components. You can expect to see a similar framework built into future versions of ASP.NET, enabling all custom controls to implement their licensing logic by using a shared infrastructure.

The LicensedLabel control you saw in Listing 17-8 uses the default licensing scheme built into ServerLicenseProvider . The code for this license provider is shown in Listing 17-9.

Listing 17-9 ServerLicenseProvider offers a framework for implementing licensing schemes specific to server controls and components.
 usingSystem; usingSystem.Collections; usingSystem.Collections.Specialized; usingSystem.ComponentModel; usingSystem.IO; usingSystem.Diagnostics; usingSystem.Globalization; usingSystem.Web; namespaceMSPress.ServerControls{ publicclassServerLicenseProvider:LicenseProvider{ privatestaticreadonly ServerLicenseCollectorLicenseCollector= newServerLicenseCollector(); protectedvirtualServerLicenseCreateLicense(Typetype, stringkey){ returnnewServerLicense(type,key); } 
 publicoverrideLicenseGetLicense(LicenseContextcontext, Typetype,objectinstance,boolallowExceptions){ ServerLicenselicense=null; stringerrorMessage=null; if(context.UsageMode==LicenseUsageMode.Designtime){ license=CreateLicense(type,String.Empty); } else{ license=LicenseCollector.GetLicense(type); if(license==null){ stringlicenseData=GetLicenseData(type); if((licenseData!=null)&& (licenseData.Length!=0)){ if(ValidateLicenseData(type,licenseData)){ ServerLicensenewLicense= CreateLicense(type,licenseData); if(ValidateLicense(newLicense, outerrorMessage)){ license=newLicense; LicenseCollector.AddLicense(type, license); } } } } else{ if(ValidateLicense(license,outerrorMessage)== false){ license=null; } } } if(allowExceptions&&(license==null)){ if(errorMessage==null){ thrownewLicenseException(type); } else{ thrownewLicenseException(type,instance, errorMessage); } } returnlicense; } protectedvirtualstringGetLicenseData(Typetype){ stringlicenseData=null; StreamlicenseStream=null; try{ licenseStream=GetLicenseDataStream(type); if(licenseStream!=null){ StreamReadersr=newStreamReader(licenseStream); licenseData=sr.ReadLine(); } } finally{ if(licenseStream!=null){ licenseStream.Close(); licenseStream=null; } } returnlicenseData; } protectedvirtualStreamGetLicenseDataStream(Typetype){ stringassemblyPart=type.Assembly.GetName().Name; stringversionPart= type.Assembly.GetName().Version.ToString(); stringrelativePath= "~/licenses/" +assemblyPart+ "/" + versionPart+ "/" +type.FullName+ ".lic"; stringlicensesFile=null; try{ licensesFile= HttpContext.Current.Server.MapPath(relativePath); if(File.Exists(licensesFile)==false){ licensesFile=null; } } catch{ } if(licensesFile!=null){ returnnewFileStream(licensesFile,FileMode.Open, FileAccess.Read,FileShare.Read); } returnnull; } protectedvirtualboolValidateLicense(ServerLicenselicense, outstringerrorMessage){ errorMessage=null; returntrue; } protectedvirtualboolValidateLicenseData(Typetype, stringlicenseData){ stringlicenseKey=type.FullName+ " islicensed."; returnString.Compare(licenseKey,licenseData,true, CultureInfo.InvariantCulture)==0; } privatesealedclassServerLicenseCollector{ privateIDictionary_collectedLicenses; publicServerLicenseCollector(){ _collectedLicenses=newHybridDictionary(); } publicvoidAddLicense(TypeobjectType, ServerLicenselicense){ if(objectType==null){ thrownewArgumentNullException("objectType"); } if(license==null){ thrownewArgumentNullException("objectType"); } _collectedLicenses[objectType]=license; } publicServerLicenseGetLicense(TypeobjectType){ if(objectType==null){ thrownewArgumentNullException("objectType"); } if(_collectedLicenses.Count==0){ returnnull; } return(ServerLicense)_collectedLicenses[objectType]; } publicvoidRemoveLicense(TypeobjectType){ if(objectType==null){ thrownewArgumentNullException("objectType"); } _collectedLicenses.Remove(objectType); } } } } 

ServerLicenseProvider is associated with a derived license object. The implementation of the ServerLicense class is shown in Listing 17-10.

Listing 17-10 ServerLicense implements the base class of licenses used along with ServerLicenseProvider .
 usingSystem; usingSystem.ComponentModel; usingSystem.Diagnostics; namespaceMSPress.ServerControls{ publicclassServerLicense:License{ privateType_type; privatestring_key; publicServerLicense(Typetype,stringkey){ _type=type; _key=key; } publicoverridestringLicenseKey{ get{ return_key; } } publicTypeLicensedType{ get{ return_type; } } publicoverridevoidDispose(){ } } } 

The implementation of ServerLicenseProvider demonstrates the following key points:

  • It derives from LicenseProvider so that it can be associated with a component by using the LicenseProviderAttribute metadata attribute. ServerLicenseProvider overrides the GetLicense method of LicenseProvider to implement its licensing scheme.

  • The class does not implement any design-time licensing. Rather, ServerLicenseProvider simply creates a new license. As we mentioned earlier, it is much more important to implement run-time licensing. If you do so, any validation scheme you implement will work, regardless of how the Web application that uses your control was built.

  • The default licensing scheme loads license information from .lic text files that are stored within a licenses directory under the root of the Web application. This approach enables the xcopy deployment model. The structure under this directory is based on the name and version of the assembly, and the name of the file is based on the full type name of the component being licensed. For example, the license file associated with the LicensedLabel class has the virtual path ~/licenses/MSPress.ServerControls/1.0.0.0/MSPress.ServerControls.LicensedLabel.lic. In this simple, default licensing scheme, the contents of the file simply read "MSPress.ServerControls.LicensedLabel is licensed."

  • ServerLicenseProvider implements a caching mechanism. Each time a license is created, it is stored in an internal cache managed by the license provider. Each time a license is requested , this cache is first looked up to try to avoid having to load license data from the .lic file. This caching mechanism allows components to incorporate complex licensing schemes without incurring a performance hit from opening the file during each request.

This simplistic model obviously isn't very useful for real licensing purposes. Therefore, ServerLicenseProvider also creates a framework for developing custom license providers with more complex validation logic. You can implement custom licensing schemes by deriving your own license provider from ServerLicenseProvider and overriding one or more of its virtual methods, which Table 17-1 describes.

Table 17-1. Overridable Methods Defined in ServerLicenseProvider

Method

Description

CreateLicense

protected virtual ServerLicense CreateLicense(Type type, string key)

Creates and returns a ServerLicense for the specified licensed type and associated validated license data. Allows derived license providers to override this method to return derived license types (for example, ­ExpiringLicenseProvider ).

GetLicenseData

protected virtual string GetLicenseData(Type type)

Retrieves license data from a license stream by reading the first line of data in the stream. Derived license providers can override this method to read from other license stores that are not stream based.

GetLicenseDataStream

protected virtual Stream GetLicenseDataStream(Type type)

Opens a stream used to read in license data. This method also contains the logic to form the virtual path to the appropriate .lic file. Derived license providers can override this method to return a custom stream implementation (for example, EncryptedLicenseProvider ).

ValidateLicense

protected virtual bool ValidateLicense(ServerLicense license, out string errorMessage)

Validates cached licenses. This validation happens each time a license is requested. Derived license providers can override this method to implement per usage “based licensing schemes (such as ExpiringLicenseProvider )

ValidateLicenseData

protected virtual bool ValidateLicenseData(Type type, string licenseData)

Validates license data before a license is created and returns true if the data is valid. Derived license providers can implement custom validation rules by overriding this method.

ExpiringLicenseProvider

The DemoLabel control shown in Listing 17-8 uses ExpiringLicenseProvider to implement a licensing scheme with a license that expires after the control has been used a specified number of times. Listings 17-11 and 17-12 contain the implementation of the license provider and its associated license.

Listing 17-11 ExpiringLicenseProvider implements a usage-based licensing scheme.
 usingSystem; usingSystem.Diagnostics; usingSystem.Globalization; namespaceMSPress.ServerControls{ publicclassExpiringLicenseProvider:ServerLicenseProvider{ protectedoverrideServerLicenseCreateLicense(Typetype, stringkey){ string[]parts=key.Split(';'); Debug.Assert(parts.Length==2); returnnewExpiringLicense(type,key, Int32.Parse(parts[1],CultureInfo.InvariantCulture)); } protectedoverrideboolValidateLicense(ServerLicenselicense, outstringerrorMessage){ errorMessage=null; ExpiringLicensetestLicense=(ExpiringLicense)license; testLicense.IncrementUsageCounter(); if(testLicense.IsExpired){ errorMessage= "TheLicensefor " + testLicense.LicensedType.Name+ " hasexpired."; returnfalse; } returntrue; } protectedoverrideboolValidateLicenseData(Typetype, stringlicenseData){ string[]parts=licenseData.Split(';'); if(parts.Length==2){ returnbase.ValidateLicenseData(type,parts[0]); } else{ returnfalse; } } } } 
Listing 17-12 ExpiringLicense is used to track usage of the license.
 usingSystem; namespaceMSPress.ServerControls{ publicclassExpiringLicense:ServerLicense{ privateint_usageLimit; privateint_usageCount; publicExpiringLicense(Typetype,stringkey,intusageLimit): base(type,key){ _usageLimit=usageLimit; } publicboolIsExpired{ get{ return_usageCount>_usageLimit; } } publicvoidIncrementUsageCounter(){ _usageCount++; } } } 

ExpiringLicenseProvider derives from ServerLicenseProvider and overrides various methods to implement a usage-based licensing scheme. This class is associated with a derived license object, ExpiringLicense , which tracks the usage limit and the usage count. The .lic file associated with DemoLabel contains the line "MSPress.ServerControls.DemoLabel is licensed.;5" as its data. The license provider implementation overrides the ValidateLicense method to extract the usage limit that is appended to the license data in the .lic file before it is checked for validity and a license object is created. The license provider overrides the ValidateLicense method to increment the usage count on the cached license object each time the object is used and to ensure that the license has not expired.

The page developer can modify the textual data in the .lic file. Encrypting license data can help improve the robustness of the licensing scheme.

EncryptedLicenseProvider

The next example shows the implementation of a different license provider. Unlike the samples you've seen so far, EncryptedLicenseProvider stores its license data in encrypted form by using the Data Encryption Standard (DES) cryptography algorithm. A powerful way to use this form of license data is to encrypt registration information provided by page developers so that only your license provider can decrypt the information to create a valid license. Listing 17-13 contains the implementation of the license provider.

Listing 17-13 EncryptedLicenseProvider works against an encrypted license file.
 usingSystem; usingSystem.Diagnostics; usingSystem.IO; usingSystem.Security.Cryptography; namespaceMSPress.ServerControls{ publicclassEncryptedLicenseProvider:ServerLicenseProvider{ //Thisisa64-bitkeygeneratedfromthestring //5FB281F6. privatestaticreadonlybyte[]encryptionKeyBytes= newbyte[]{ 0x35,0x46,0x42,0x32,0x38,0x31,0x46,0x36}; protectedoverrideStreamGetLicenseDataStream(Typetype){ StreambaseStream=base.GetLicenseDataStream(type); if(baseStream==null){ returnnull; } DESCryptoServiceProviderdes= newDESCryptoServiceProvider(); des.Key=encryptionKeyBytes; des.IV=encryptionKeyBytes; ICryptoTransformdesDecryptor=des.CreateDecryptor(); returnnewCryptoStream(baseStream,desDecryptor, CryptoStreamMode.Read); } } } 

EncryptedLicenseProvider derives from ServerLicenseProvider and overrides the GetLicenseDataStream method to wrap the underlying Stream created by the base class's corresponding method to read in the license data, with a CryptoStream to decrypt license data as it is read in. The CryptoStream used in the sample employs the DES cryptography algorithm with a 64-bit encryption key.

The .lic file associated with the EncryptedLicensedLabel control shown in Listing 17-8 is encrypted by using the same key. We have provided the code for the encryption tool, EncLicGen.exe, in EncryptedLicenseGenerator.cs in this book's sample files. The data contained within the .lic file is still "MSPress.ServerControls.EncryptedLicensedLabel is licensed.", but it appears as gibberish because of the encryption. Figure 17-4 shows how this data looks.

Figure 17-4. The encrypted data within MSPress.Server ­Controls.EncryptedLicensedLabel.lic

graphics/f17hn04.jpg

Encryption adds another level of robustness to the licensing scheme. A real licensing scheme you develop for your components might encrypt a combination of your own data and the user 's registration information, instead of encrypting a fixed static string as this sample does.

Note that the encryption key is embedded in the code itself in this sample. This is permissible to some degree because the licensing framework is designed to make it harder ”not impossible ”to break licenses. The Win32 security APIs provide more sophisticated mechanisms for storing encryption keys.



Developing Microsoft ASP. NET Server Controls and Components
Developing Microsoft ASP.NET Server Controls and Components (Pro-Developer)
ISBN: 0735615829
EAN: 2147483647
Year: 2005
Pages: 183

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net