In this chapter, you learn how to configure your ASP.NET applications. In the first section, you are provided with an overview of the different sections contained in a web configuration file. You also learn how to modify web configuration files by using both the Web Site Administration Tool and the ASP.NET Microsoft Management Console Snap-In.
Next, you learn how to manipulate configuration settings programmatically with the Configuration API. We discuss how you can both retrieve and modify configuration settings. You also learn how to work with configuration settings located at a remote website.
You also learn how to add custom configuration sections to the web configuration file. You learn how to register custom configuration sections and interact with custom configuration sections with the Configuration API.
Finally, we discuss the very important topic of protecting your configuration files. You learn how to encrypt different sections of a configuration file so that they cannot be read by human eyes. You also learn how you can deploy an encrypted configuration file from one server to another.
ASP.NET uses a hierarchical system of configuration. At the top of the hierarchy is the Machine.config
file. This file contains all the default configuration settings for ASP.NET applications and all other types of applications built with the .NET Framework.
The Machine.config
file is located at the following path:
WINDOWSMicrosoft.NETFramework[version]CONFIGMachine.config
This same folder also contains a Web.config
file. The Web.config
file contains settings specific to ASP.NET applications. The Web.config
file overrides particular settings in the Machine.config
file.
The CONFIG
folder includes the following six files:
Machine.config
—. Contains the actual configuration settings.
Machine.config.default
—. Contains the default values for all configuration settings.
Machine.config.comments
—. Contains comments on each configuration setting.
Web.config
—. Contains the actual configuration settings.
Web.config.default
—. Contains the default values for all configuration settings.
Web.config.comments
—. Contains comments on each configuration setting.
Only the Machine.config
and Web.config
files are actually used. The other files are there for the purpose of documentation.
You can place a Web.config
file in the root folder of a website, such as the wwwroot folder. A Web.config
file located in the root folder of a website contains settings that apply to all applications contained in the website.
You also can place a Web.config
file in the root of a particular application. In that case, the Web.config
file has application scope.
Finally, you can place a Web.config
file in an application subfolder. In that case, the Web.config
file applies to all pages in that folder and below.
When an ASP.NET application starts, this hierarchy of configuration files is merged and cached in memory. A file dependency is created between the cached configuration settings and the file system. If you make a change to any of the configuration files in the hierarchy, the new configuration settings are loaded into memory automatically.
When an ASP.NET page reads a configuration setting, the setting is read from memory. This means that the ASP.NET Framework can read configuration settings, such as connection strings, very efficiently.
Furthermore, when you make a change to a configuration setting, you don’t need to stop and restart an application manually for the new setting to take effect. The ASP.NET Framework reloads the cached configuration settings automatically when the configuration settings are changed on the file system. (The one exception to this is modifications to the processModel
section.)
Modifying most configuration settings results in an application restart. Any data stored using the cache or in-process Session
state is lost and must be reloaded. You can get around this issue by using external configuration files. See the section “Placing Configuration Settings in an External File” later in this chapter.
The configuration files are XML files. You can modify configuration settings by opening the Machine.config
file or a Web.config
file and modifying a setting in Notepad. Alternatively, you can change many of the configuration settings (but not all) by using either the Web Site Administration Tool or the ASP.NET Microsoft Management Console Snap-In.
If you are using Visual Web Developer (or Visual Studio .NET), then you can modify certain configuration settings with the Web Site Administration Tool. This tool provides you with a form interface for making configuration changes (see Figure 26.1).
You open the Web Site Administration Tool by selecting the menu option Website, ASP.NET Configuration. Selecting this option opens a browser window that contains the tool.
There is a bug in the first release of ASP.NET 2.0. If you open the Web Site Administration Tool, then you’ll lose Intellisense when you open the Web.config
file directly in Visual Web Developer. The Web Site Administration Tool adds an xmlns
attribute to the opening <configuration>
tag. If you want to get Intellisense working again, then you need to remove the xmlns
attribute.
The Web Site Administration Tool has the following four tabs:
Home—. This tab contains links to the other tabs.
Security—. This tab enables you to configure authentication, authorization, and the Role Manager.
Application—. This tab enables you to create and manage application settings, configure SMTP settings, and enable application tracing, debugging, and error pages. You also can use this tab to take your application offline.
Provider—. This tab enables you to select a provider for Membership and the Role Manager.
Under the Application tab, you can click the link to take your application offline. When you click this link, the following httpRuntime
element is added to your web configuration file:
<httpRuntime enable="false" />
This setting causes the Application Domain associated with the ASP.NET application to refuse any requests. When an application is offline, all requests result in a 404—Not Found error message. You might want to take your application offline, for example, to prevent people from requesting pages while you perform updates to your application.
You also can take an ASP.NET application offline by adding a file with the name app_offline.htm
to the root of your application.
The Web Site Administration Tool is implemented as an ASP.NET application. Behind the scenes, it uses the Configuration API that is discussed later in this chapter. You can view the entire source code for the Web Site Administration Tool by navigating to the following folder:
WINDOWSMicrosoft.NETFramework[version]ASP.NETWebAdminFiles
You also can make configuration changes with the ASP.NET Microsoft Management Console (MMC) Snap-In tool (see Figure 26.2). You can open the ASP.NET MMC Snap-In by following these steps:
Open Internet Information Services from Start, Control Panel, Administrative Tools.
Open the property sheet for either a website or a virtual directory.
Select the ASP.NET tab.
Click the Edit Configuration (or Edit Global Configuration) button.
The ASP.NET MMC Snap-In includes the following tabs:
General—. Enables you to configure connection strings and application settings.
Custom Errors—. Enables you to configure custom error pages.
Authorization—. Enables you to configure authorization rules.
Authentication—. Enables you to configure Forms, Windows, or Passport authentication.
Application—. Enables you to configure application settings such as application-wide Master Pages and Themes.
State Management—. Enables you to configure Session state.
Locations—. Enables you to apply configuration settings to a particular folder or page.
Behind the scenes, the ASP.NET MMC Snap-In uses the Configuration API to make changes to web configuration files.
All the configuration sections in the Machine.config
or Web.config
file related to ASP.NET are contained in the <system.web>
section group. Here is a complete list of the 36 ASP.NET configuration sections and a brief explanation of the purpose of each section:
anonymousIdentification
—. Enables you to configure anonymous user identification, which is used, for example, by the Profile
object. See Chapter 22, “Maintaining Application State.”
authentication
—. Enables you to configure authentication. See Chapter 21, “Using ASP.NET Membership.”
authorization
—. Enables you to configure authorization. See Chapter 21.
browserCaps
—. Enables you to configure the lookup of browser capabilities.
caching
—. Enables you to configure caching. See Chapter 23, “Caching Application Pages and Data.”
clientTarget
—. Enables you to configure aliases for different clients (browsers).
compilation
—. Enables you to configure how ASP.NET applications are compiled. For example, you can specify whether or not an application is compiled in debug mode.
customErrors
—. Enables you to configure custom error pages.
deployment
—. Enables you to specify whether an ASP.NET application is deployed in retail mode.
deviceFilters
—. Enables you to configure device filters.
globalization
—. Enables you to configure the Culture
, UICulture
, and other attributes related to building multi-lingual web applications. See Chapter 24, “Localizing Applications for Multiple Languages.”
healthMonitoring
—. Enables you to configure Health Monitoring. See the final section of this chapter.
hostingEnvironment
—. Enables you to configure ASP.NET application properties such as the application idle timeout.
httpCookies
—. Enables you to configure how cookies are sent to the browser. See Chapter 22.
httpHandlers
—. Enables you to configure HTTP Handlers. See Chapter 25, “Working with the HTTP Runtime.”
httpRuntime
—. Enables you to configure properties of the HTTP Runtime, such as the number of threads maintained in the thread pool.
httpModules
—. Enables you to configure HTTP Modules. See Chapter 25.
identity
—. Enables you to configure the identity of the ASP.NET application account.
machineKey
—. Enables you to configure encryption keys used by Membership
and Session
state. See Chapter 21 and Chapter 22.
membership
—. Enables you to configure ASP.NET Membership. See Chapter 21.
mobileControls
—. Enables you to configure adapters used with ASP.NET mobile controls.
pages
—. Enables you to configure page properties such as the website Master Page and Theme. See Chapter 5, “Designing Websites with Master Pages,” and Chapter 6, Designing Websites with Themes.”
processModel
—. Enables you to configure the ASP.NET process.
profile
—. Enables you to configure the Profile object. See Chapter 22.
roleManager
—. Enables you to configure the Role Manager. See Chapter 21.
securityPolicy
—. Enables you to map security policy files to trust levels.
sessionPageState
—. Enables you to configure how mobile devices store Session
state.
sessionState
—. Enables you to configure Session
state. See Chapter 22.
siteMap
—. Enables you to configure Site Maps. See Chapter 18, “Using Site Maps.”
trace
—. Enables you to configure page and application tracing.
trust
—. Enables you to configure Code Access Security (CAS) for an ASP.NET application.
urlMappings
—. Enables you to remap page requests to new pages. See Chapter 19, “Advanced Navigation.”
webControls
—. Enables you to specify the location of client-script files used by web controls.
webParts
—. Enables you to configure Web Parts. See Part VIII, “Building Applications with Web Parts.”
webServices
—. Enables you to configure web services.
xhtmlConformance
—. Enables you to configure the level of XHTML conformance of the XHTML rendered by web controls.
By default, the settings in a Machine.config
or Web.config
file are applied to all pages in the same folder and below. However, if you have the need, you can also apply configuration settings to a particular path. For example, you can apply configuration settings to a particular subfolder or even a particular page.
You apply configuration settings to a particular path by using the <location>
element. For example, the web configuration file in Listing 26.1 enables password-protection for a single file named Secret.aspx
.
If you attempt to request the Secret.aspx
page, you are redirected to the Login.aspx
page. However, none of the other files in the same application are password protected by the configuration file.
The <location>
element must be added as an immediate child of the <configuration>
element. You can’t, for example, add the <location>
element within a <system.web>
element. You must surround the <system.web>
element with the <location>
element.
You can create the web configuration file in Listing 26.1 by selecting the menu option Website, Add New Item, and selecting the Web Configuration File template. Alternatively, you can add the appSettings
section by using either the Web Site Administration Tool or the ASP.NET MMC Snap-In. Both tools enable you to enter values for the appSettings
section through a user-friendly interface.
You can lock configuration settings so that they cannot be overridden at a lower level in the configuration hierarchy. For example, you might want to require that no application running on your production server execute in debug mode. In that case, you can lock the debug configuration setting in a website Web.config
file, the root Web.config
file, or the Machine.config
file.
You can lock a configuration setting in multiple ways. The Web.config
file in Listing 26.2 illustrates how you can lock a setting by using the allowOverride="false"
attribute of the <location>
element.
The configuration file in Listing 26.2 locks the compilation element. If you attempt to add a configuration file that sets the debug
attribute to the value true
, and the configuration file is located below the configuration file in Listing 26.2, then an exception is raised (see Figure 26.3).
One problem with the configuration file in Listing 26.2 is that it locks the entire compilation element. If you attempt to change any attribute of the compilation element at a lower level in the configuration hierarchy, then an exception is raised.
You can add any of the following attributes to a particular configuration element to lock either the entire element or one or more of its attributes:
lockAllAttributesExcept
—. Enables you to lock all attributes except those listed as the value of this attribute. You can specify multiple attributes to exclude in a comma-delimited list.
lockAllElementsExcept
—. Enables you to lock all child elements of the current element except those listed as the value of this attribute. You can specify multiple elements to exclude in a comma-delimited list.
lockAttributes
—. Enables you to lock multiple attributes. You can specify the attributes to lock in a comma-delimited list.
lockElement
—. Enables you to lock multiple child elements. You can specify the child elements to lock in a comma-delimited list.
lockItem
—. Enables you to lock the current element.
For example, the web configuration file in Listing 26.3 locks the debug
attribute, and only the debug
attribute, of the <compilation>
element.
You can add custom configuration settings to the web configuration file easily by taking advantage of the appSettings
section. The appSettings
section contains a list of key and value pairs.
For example, the web configuration file in Listing 26.4 contains a welcome message and a copyright notice.
You can retrieve values from the appSettings
section either programmatically or declaratively. The page in Listing 26.5 illustrates both approaches (see Figure 26.4).
Example 26.5. ShowAppSettings.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.Web.Configuration" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Sub Page_Load() lblWelcome.Text = WebConfigurationManager.AppSettings("welcome") End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show AppSettings</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label id="lblWelcome" Runat="server" /> <hr /> <asp:Literal id="ltlCopyright" Text="<%$ AppSettings:copyright %>" Runat="server" /> </div> </form> </body> </html>
In Listing 26.5, the welcome message is retrieved programmatically from the WebConfigurationManager.AppSettings
property. The value retrieved is assigned to a Label
control. Notice that the System.Web.Configuration
namespace must be imported before you can use the WebConfigurationManager
class.
You retrieve the copyright notice declaratively by using the AppSettingsExpressionBuilder
. The following expression is used to retrieve the value of the copyright key:
<%$ AppSettings: copyright %>
You can place particular configuration sections in an external file. You might want to do this for a couple of reasons. First, you can make a configuration file more manageable by dividing it into multiple files. Also, when you place configuration information in a separate file, you can prevent application restarts when you change a configuration setting.
Every configuration element includes a configSource
attribute. You can assign a path to a file as the value of the configSource
attribute.
For example, the web configuration file in Listing 26.6 uses the configSource
attribute in its <appSettings>
element.
The appSettings
are stored in the external file in Listing 26.7.
Normally, modifying a web configuration file results in your ASP.NET application restarting. Any data stored in Session State or the Cache
object is lost.
However, the appSettings
section is declared in the Machine.config
file with a restartOnExternalChanges="false"
attribute. This attribute prevents your application from restarting when a change is made to the appSettings
section in an external configuration file. If you modify the file in Listing 26.6, for example, your application won’t restart.
The CD that accompanies this book includes a page named ShowAppStartTime.aspx
, which displays the time that the current ASP.NET application started. You can use this file to detect when a modification made to a web configuration file caused an application restart. (The application start time is retrieved in the Application_Start()
event handler in the Global.asax
file.)
The Configuration API enables you to retrieve and modify configuration settings. You can use the Configuration API to modify web configuration files on the local machine or a remote machine.
If you are responsible for maintaining a large number of websites, the Configuration API can make your life much easier. You can build administrative tools that enable you to make configuration changes quickly to multiple applications. You can use the Configuration API in an ASP.NET page, or you can build command-line tools or Windows Forms applications that use the Configuration API.
The Configuration API is exposed by the WebConfigurationManager
class (located in the System.Web.Configuration
namespace). This class supports the following properties:
AppSettings
—. Exposes all the settings from the appSettings
section.
ConnectionStrings
—. Exposes all the settings from the connectionStrings
section.
The WebConfigurationManager
also supports the following methods:
GetSection
—. Retrieves a configuration section relative to the current page or a supplied virtual path.
GetWebApplicationSection
—. Retrieves a configuration section from the current web application root web configuration file.
OpenMachineConfiguration
—. Retrieves a Machine.config
file on either the local machine or a remote server.
OpenMappedMachineConfiguration
—. Retrieves a Machine.config
file by using a particular file mapping.
OpenMappedWebConfiguration
—. Retrieves a web configuration file by using a particular file mapping.
OpenWebConfiguration
—. Retrieves a Web.config
file on either the local machine or a remote server.
Almost every configuration section in the web configuration file has a corresponding class in the .NET Framework that represents the configuration section. These classes provide you with a strongly typed representation of each configuration section.
For example, corresponding to the <authentication>
section in the web configuration file, there is a System.Web.Configuration.AuthenticationSection
class. Corresponding to the <pages>
section in the web configuration file, there is a System.Web.Configuration.PagesSection
class. Each of these classes expose properties that correspond to all the attributes you can set in the web configuration file.
When an ASP.NET application starts, the application merges all the configuration settings in the configuration hierarchy to create one representation of the configuration settings. A particular configuration setting might have different values at different levels in the hierarchy. You can use the methods of the WebConfigurationManager
class to get the value of a configuration setting at any level in the hierarchy.
The WebConfigurationManager.GetWebApplicationSection()
method always retrieves a configuration setting from the application root Web.config
file. For example, the page in Listing 26.8 displays whether debugging is enabled.
Example 26.8. ShowConfigApp.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.Web.Configuration" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Sub Page_Load() Dim section As CompilationSection = CType( WebConfigurationManager.GetWebApplicationSection("system.web/compilation"), CompilationSection) lblDebug.Text = section.Debug.ToString() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Config App</title> </head> <body> <form id="form1" runat="server"> <div> Debug Mode: <asp:Label id="lblDebug" Runat="server" /> </div> </form> </body> </html>
The GetWebApplication()
method returns an object. You must cast the value returned by this method to a particular configuration section type. In Listing 26.8, the value returned by this method is cast to an instance of the CompilationSection
type.
Realize that you will get the same result when the page in Listing 26.8 is located in different subfolders. For example, debugging might not be enabled in a root configuration file, but it might be enabled in a configuration file in a particular subfolder. However, if you call the GetWebApplicationSection()
method, the method always returns the configuration setting for the application root Web.config
file.
If you want to get the value of a configuration setting relative to the folder in which the page executes, then you can use the GetSection()
method instead of the GetWebApplicationSection()
method. The page in Listing 26.9 is located in a subfolder. The page displays the value of the debug setting retrieved from both the GetWebApplicationSection()
method and the GetSection()
method (see Figure 26.5).
Example 26.9. SubFolderShowConfigRelative.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.Web.Configuration" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Sub Page_Load() Dim section As CompilationSection = CType( WebConfigurationManager.GetSection("system.web/compilation"), CompilationSection) lblDebug1.Text = section.Debug.ToString() section = CType(WebConfigurationManager.GetWebApplicationSection("system.web/compilation"), CompilationSection) lblDebug2.Text = section.Debug.ToString() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Config Relative</title> </head> <body> <form id="form1" runat="server"> <div> GetSection Debug: <asp:Label id="lblDebug1" Runat="server" /> <br /><br /> GetWebApplicationSection Debug: <asp:Label id="lblDebug2" Runat="server" /> </div> </form> </body> </html>
When you request the page in Listing 26.9, different values are displayed by the GetSection()
method and GetWebApplicationSection()
method. The method displays the configuration setting relative to the current directory. The second method displays the configuration setting from the application root Web.config
file.
If you want to retrieve the value of a configuration setting for a particular path, then you can use the overload of the GetSection()
method that accepts a path parameter. The page in Listing 26.10 iterates through all the immediate subfolders contained in the current application and displays whether debugging is enabled (see Figure 26.6).
Example 26.10. ShowConfigPath.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.IO" %> <%@ Import Namespace="System.Web.Configuration" %> <%@ Import Namespace="System.Collections.Generic" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Sub Page_Load() Dim results As New Dictionary(Of String, Boolean)() Dim rootDir As New DirectoryInfo(Request.PhysicalApplicationPath) Dim dirs As DirectoryInfo() = rootDir.GetDirectories() For Each dir As DirectoryInfo In dirs Dim path As String = "~/" & dir.Name Dim section As CompilationSection = CType(WebConfigurationManager.GetSection("system.web/compilation", path), CompilationSection) results.Add(path, section.Debug) Next grdResults.DataSource = results grdResults.DataBind() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Config Path</title> </head> <body> <form id="form1" runat="server"> <div> <asp:GridView id="grdResults" AutoGenerateColumns="false" Runat="server"> <Columns> <asp:BoundField DataField="Key" HeaderText="Path" /> <asp:BoundField DataField="Value" HeaderText="Debug Mode" /> </Columns> </asp:GridView> </div> </form> </body> </html>
If you want to open a particular configuration file, then you can use one of the Open
methods exposed by the WebConfigurationManager
class. For example, the page in Listing 26.11 uses the OpenMachineConfiguration()
method to open the Machine.config
file and display the default value for the authentication mode setting.
Example 26.11. ShowConfigMachine.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.Web.Configuration" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Sub Page_Load() Dim config As Configuration = WebConfigurationManager.OpenMachineConfiguration() Dim section As AuthenticationSection = CType(config.GetSection("system.web/authentication"), AuthenticationSection) lblMode.Text = section.Mode.ToString() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Config Machine</title> </head> <body> <form id="form1" runat="server"> <div> Authentication Mode Default Value: <asp:Label id="lblMode" Runat="server" /> </div> </form> </body> </html>
You can use the WebConfigurationManager
class to display configuration information for other websites located on the same server. For example, the page in Listing 26.12 displays a list of all the virtual directories contained in the default website. You can select a virtual directory and view the authentication mode associated with the virtual directory (see Figure 26.7).
Example 26.12. ShowConfigSites.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.Web.Configuration" %> <%@ Import Namespace="System.DirectoryServices" %> <%@ Import Namespace="System.Collections.Generic" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Const sitePath As String = "IIS://localhost/W3SVC/1/ROOT" Sub Page_Load() If Not Page.IsPostBack Then dropVDirs.DataSource = GetVirtualDirectories() dropVDirs.DataBind() End If End Sub Private Function GetVirtualDirectories() As List(Of String) Dim dirs As New List(Of String)() Dim site As New DirectoryEntry(sitePath) Dim vdirs As DirectoryEntries = site.Children For Each vdir As DirectoryEntry In vdirs If vdir.SchemaClassName = "IIsWebVirtualDir" Then Dim vPath As String = vdir.Path.Remove(0, sitePath.Length) dirs.Add(vPath) End If Next Return dirs End Function Protected Sub btnSelect_Click(ByVal sender As Object, ByVal e As EventArgs) Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration(dropVDirs.SelectedValue) Dim section As AuthenticationSection = CType( config.GetSection("system.web/authentication"), AuthenticationSection) lblAuthenticationMode.Text = section.Mode.ToString() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Config Sites</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label id="lblVirtualDirectory" Text="Virtual Directory:" AssociatedControlID="dropVDirs" Runat="server" /> <asp:DropDownList id="dropVDirs" Runat="server" /> <asp:Button id="btnSelect" Text="Select" OnClick="btnSelect_Click" Runat="server" /> <hr /> Authentication Mode: <asp:Label id="lblAuthenticationMode" Runat="server" /> </div> </form> </body> </html>ShowConfigSites
The list of virtual directories is retrieved with the classes from the System.DirectoryServices
namespace. When you select a virtual directory, the OpenWebConfiguration()
method is called with the path to the virtual directory to get the configuration information.
You can use the WebConfigurationManager
class to open Machine.config
or Web.config
files located on remote web servers. However, before you can do this, you must perform one configuration step. You must enable the remote server to accept remote configuration connections by executing the following command from a command prompt:
aspnet_regiis -config+
To disable remove configuration connections, execute the following command:
aspnet_regiis -config-
The aspnet_regiis
tool is located in the following path:
WINDOWSMicrosoft.NETFramework[version]aspnet_regiis.exe
If you open the SDK Command Prompt, then you don’t need to navigate to the Microsoft.NET folder to execute the aspnet_regiis
tool.
After you make this modification to a remote server, you can retrieve (and modify) configuration settings on the remote server by using one of the Open methods exposed by the WebConfigurationManager
class. For example, the page in Listing 26.13 contains a form that enables you to enter a server, username, and password. When you submit the form, the page connects to the remote server and retrieves its Machine.config
file. The page displays the current value of the remote server’s authentication mode (see Figure 26.8).
Example 26.13. ShowConfigRemote.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.Web.Configuration" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As EventArgs) Try Dim config As Configuration = WebConfigurationManager.OpenMachineConfiguration(Nothing, txtServer.Text,txtUserName.Text, txtPassword.Text) Dim section As AuthenticationSection = CType(config.GetSection("system.web/authentication"), AuthenticationSection) lblAuthenticationMode.Text = section.Mode.ToString() Catch ex As Exception lblAuthenticationMode.Text = ex.Message End Try End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Config Remote</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label id="lblServer" Text="Server:" AssociatedControlID="txtServer" Runat="server" /> <br /> <asp:TextBox id="txtServer" Runat="server" /> <br /><br /> <asp:Label id="lblUserName" Text="User Name:" AssociatedControlID="txtUserName" Runat="server" /> <br /> <asp:TextBox id="txtUserName" Runat="server" /> <br /><br /> <asp:Label id="lblPassword" Text="Password:" AssociatedControlID="txtPassword" Runat="server" /> <br /> <asp:TextBox id="txtPassword" TextMode="Password" Runat="server" /> <br /><br /> <asp:Button id="btnSubmit" Text="Submit" OnClick="btnSubmit_Click" Runat="server" /> <hr /> Authentication Mode: <asp:Label id="lblAuthenticationMode" Runat="server" /> </div> </form> </body> </html>
You can use the page in Listing 26.13 even when the web server is located in some distant part of the Internet. You can enter a domain name or IP address in the server field.
When you use one of the WebConfigurationManager
Open methods—such as the OpenMachineConfiguration()
or OpenWebConfiguration()
methods—the method returns an instance of the Configuration
class. This class supports the following properties:
AppSettings
—. Returns the appSettings
configuration section.
ConnectionStrings
—. Returns the connectionStrings
configuration section.
EvaluationContext
—. Returns an instance of the ContextInformation
class that enables you to determine the context of the configuration information.
FilePath
—. Returns the physical file path to the configuration file.
HasFile
—. Returns True
when there is a file that corresponds to the configuration information.
Locations
—. Returns a list of locations defined by the configuration.
NamespaceDeclared
—. Returns True
when the configuration file includes a namespace declaration.
RootSectionGroup
—. Returns the root section group.
SectionGroups
—. Returns the child section groups contained by this configuration.
Sections
—. Returns the child sections contained by this configuration.
The Configuration
class also supports the following methods:
GetSection
—. Enables you to return the specified configuration section.
GetSectionGroup
—. Enables you to return the specified configuration section group.
Save
—. Enables you to save any configuration changes.
SaveAs
—. Enables you to save the configuration as a new file.
A configuration file contains two basic types of entities: section groups and sections. For example, the <system.web>
element in a configuration file represents a section group. The <system.web>
section group contains child sections such as the <authentication>
and <httpRuntime>
sections.
You can use the Configuration.RootSectionGroup
property to get the primary section group in a configuration file. You can use the SectionGroups
property to return all of a section group’s child section groups and the Sections
property to return all of a section group’s child sections.
For example, the page in Listing 26.14 recursively displays the contents of the Machine.config
file in a TreeView
control (see Figure 26.9).
Example 26.14. ShowConfigContents.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.Web.Configuration" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> sub Page_Load() ' Add first node Dim parentNode As New TreeNode("configuration") TreeView1.Nodes.Add(parentNode) ' Start from the root section group Dim config As Configuration = WebConfigurationManager.OpenMachineConfiguration() ' Show child section groups AddChildSectionGroups(parentNode, config.RootSectionGroup) ' Show child sections AddChildSections(parentNode, config.RootSectionGroup) end sub Private Sub AddChildSectionGroups(ByVal parentNode As TreeNode, ByVal parentConfigSectionGroup As ConfigurationSectionGroup) For Each configSectionGroup As ConfigurationSectionGroup In parentConfigSectionGroup.SectionGroups Dim childNode As New TreeNode(configSectionGroup.SectionGroupName) parentNode.ChildNodes.Add(childNode) AddChildSectionGroups(childNode, configSectionGroup) AddChildSections(childNode, configSectionGroup) Next End Sub Private Sub AddChildSections(ByVal parentNode As TreeNode, ByVal parentConfigSectionGroup As ConfigurationSectionGroup) For Each configSection As ConfigurationSection In parentConfigSectionGroup.Sections Dim childNode As New TreeNode(configSection.SectionInformation.Name) parentNode.ChildNodes.Add(childNode) Next End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Config Contents</title> </head> <body> <form id="form1" runat="server"> <div> <asp:TreeView id="TreeView1" Runat="server" /> </div> </form> </body> </html>
You can use the WebConfigurationManager
class not only when opening a configuration file to read the values of various configuration settings. You also can use the WebConfigurationManager
class to modify existing configuration settings or add new ones.
The Configuration
class supports two methods for saving configuration information: the Save()
and SaveAs()
methods. For example, the page in Listing 26.15 enables you to turn on and off debugging for an application (see Figure 26.10).
Example 26.15. ShowConfigModify.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.Web.Configuration" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> sub Page_Load() if Not Page.IsPostBack Then Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath) Dim section As CompilationSection = CType(config.GetSection("system.web/compilation"), CompilationSection) chkDebug.Checked = section.Debug end if end sub Protected Sub btnUpdate_Click(ByVal sender As Object, ByVal e As EventArgs) Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath) Dim section As CompilationSection = CType(config.GetSection("system.web/compilation"), CompilationSection) section.Debug = chkDebug.Checked config.Save(ConfigurationSaveMode.Modified) End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Config Modify</title> </head> <body> <form id="form1" runat="server"> <div> <asp:CheckBox id="chkDebug" Text="Enable Debugging?" Runat="server" /> <br /><br /> <asp:Button id="btnUpdate" Text="Update" OnClick="btnUpdate_Click" Runat="server" /> </div> </form> </body> </html>
The page in Listing 26.15 loads the application root Web.config
file with the help of the OpenWebConfiguration()
method (the Nothing
parameter causes the root Web.config
file to be loaded). Next, the value of the Compilation.Debug
property is modified. Finally, the Save()
method is called to save this change.
When you call the Save()
method, you can pass a ConfigurationSaveMode
parameter to the method. This parameter can have the following values:
Full
—. Saves all configuration settings, regardless of whether they have been modified.
Minimal
—. Saves only those configuration settings that are different from their inherited value.
Modified
—. Saves only those configuration settings that have been modified.
To use the Save()
or SaveAs()
methods, the account associated with the page must have Write
permissions for the folder where the configuration file is saved. By default, when pages are served from Internet Information Server, ASP.NET pages execute in the security context of the NETWORK SERVICE account (in the case of Windows Server 2003) or the ASPNET account (in the case of other operating systems). By default, neither of these accounts have permissions to save configuration changes.
To make things more confusing, when pages are served from the web server included with Visual Web Developer, the pages are always served in the security context of the current user.
There are multiple ways that you can get around this permission problem. First, remember that you can use many of the methods of the WebConfigurationManager
class from a console application or a Windows Forms application. If you build this type of application, then you can sidestep these security issues.
Another option is to enable per-request impersonation for your ASP.NET application. When impersonation is enabled, an ASP.NET page executes within the security context of the user making the page request. If the user account has permissions to write to the file system, then the page has permissions to write to the file system.
The web configuration file in Listing 26.16 enables impersonation.
If you add the configuration file in Listing 26.16 to the same folder that contains the file in Listing 26.15, then you will be able to make modifications to configuration files.
When you are provisioning new websites, you often need to create a new virtual directory. The Configuration API doesn’t provide you with any help here.
However, you can create new virtual directories (and applications) by taking advantage of the classes in the System.DirectoryServices
namespace. These classes enable you to use Active Directory Services Interface (ADSI) to modify properties of Internet Information Server.
You also can manipulate Internet Information Server properties by using Windows Management Instrumentation (WMI). For more information, see the topic "Using WMI to Configure IIS" at the Microsoft MSDN website (msdn.microsoft.com).
Before you can use the classes from the System.DirectoryServices
namespace, you need to add a reference to the System.DirectoryServices.dll
assembly. In Visual Web Developer, select the menu option Website, Add Reference, and select System.DirectoryServices.dll
.
For example, the page in Listing 26.17 enables you to provision a new ASP.NET application (see Figure 26.11). The page creates a new virtual directory and a new application. The page also creates a new web configuration file in the virtual directory that contains the default language and debug settings you specify.
Example 26.17. ProvisionSite.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.IO" %> <%@ Import Namespace="System.DirectoryServices" %> <%@ Import Namespace="System.Web.Configuration" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Const wwwroot As String = "c:Inetpub" Const sitePath As String = "IIS://localhost/W3SVC/1/ROOT" Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As EventArgs) Dim NewFolder As String = Path.Combine(wwwroot, txtVirtualDir.Text) CreateVirtualDirectory(NewFolder, txtVirtualDir.Text, txtVirtualDir.Text) CreateConfiguration(txtVirtualDir.Text) ' Show link to new site lnkNewSite.NavigateUrl = "http://localhost/" & txtVirtualDir.Text lnkNewSite.Target = "_top" lnkNewSite.Visible = True End Sub Private Sub CreateVirtualDirectory(ByVal folderPath As String, ByVal virtualDirectoryName As String, ByVal appFriendlyName As String) ' Create new Folder Directory.CreateDirectory(folderPath) ' Create Virtual Directory Dim vRoot As New DirectoryEntry(sitePath) Dim vDir As DirectoryEnTry = vRoot.Children.Add(virtualDirectoryName, "IIsWebVirtualDir") vDir.CommitChanges() vDir.Properties("Path").Value = folderPath vDir.Properties("DefaultDoc").Value = "Default.aspx" vDir.Properties("DirBrowseFlags").Value = 2147483648 vDir.CommitChanges() vRoot.CommitChanges() ' Create Application (Isolated) vDir.Invoke("AppCreate2", 1) vDir.Properties("AppFriendlyName").Value = appFriendlyName vDir.CommitChanges() End Sub Private Sub CreateConfiguration(ByVal virtualPath As String) ' Open configuration Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration("/" & virtualPath) ' Set language and debug setting Dim section As CompilationSection = CType(config.GetSection("system.web/compilation"), CompilationSection) section.DefaultLanguage = rdlLanguage.SelectedItem.Text section.Debug = chkDebug.Checked ' Save configuration config.Save(ConfigurationSaveMode.Modified) End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Provision Site</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label id="lblVirtualDir" Text="Virtual Directory:" AssociatedControlID="txtVirtualDir" Runat="server" /> <br /> <asp:TextBox id="txtVirtualDir" Runat="server" /> <br /><br /> <asp:Label id="lblLanguage" Text="Default Language:" AssociatedControlID="rdlLanguage" Runat="server" /> <asp:RadioButtonList id="rdlLanguage" Runat="server"> <asp:ListItem Text="VB" Selected="True" /> <asp:ListItem Text="C#" /> </asp:RadioButtonList> <br /> <asp:CheckBox id="chkDebug" Text="Enable Debugging" Runat="server" /> <br /><br /> <asp:Button id="btnSubmit" Text="Submit" OnClick="btnSubmit_Click" Runat="server" /> <hr /> <asp:HyperLink id="lnkNewSite" Visible="false" Text="Go to New Site" Runat="server" /> </div> </form> </body> </html>
To use the page in Listing 26.17, you’ll need adequate permissions. You can enable per-request impersonation by adding the file in Listing 26.16 to the same folder as the page in Listing 26.17.
You can add custom configuration sections to a web configuration file. You can use a custom configuration section to store whatever information you want.
For example, if you need to manage a large number of database connection strings, then you might want to create a custom database connection string configuration section. Or, if you want to follow the Provider Model and implement a custom provider, then you need to create a custom configuration section for your provider.
You create a custom configuration section by inheriting a new class from the base ConfigurationSection
class. For example, the class in Listing 26.18 represents a simple custom configuration section.
Example 26.18. App_CodeDesignSection.vb
Imports System Imports System.Configuration Imports System.Drawing Namespace AspNetUnleashed Public Class DesignSection Inherits ConfigurationSection <ConfigurationProperty("backcolor", DefaultValue:="lightblue", IsRequired:=True)> _ Public Property BackColor() As Color Get Return CType(Me("backcolor"), Color) End Get Set(ByVal Value As Color) Me("backcolor") = Value End Set End Property <ConfigurationProperty("styleSheetUrl", DefaultValue:="~/styles/style.css",IsRequired:=True)> _ <RegexStringValidator(".css$")> _ Public Property StyleSheetUrl() As String Get Return CType(Me("styleSheetUrl"), String) End Get Set(ByVal Value As String) Me("styleSheetUrl") = Value End Set End Property Public Sub New(ByVal backcolor As Color, ByVal styleSheetUrl As String) Me.BackColor = backcolor Me.StyleSheetUrl = styleSheetUrl End Sub Public Sub New() End Sub End Class End Namespace
The class in Listing 26.18 represents a Design configuration section. This section has two properties: BackColor
and StyleSheetUrl
.
Notice that both properties are decorated with ConfigurationProperty
attributes. The ConfigurationProperty
attribute is used to map the property to an element attribute in the configuration file. When you declare the ConfigurationProperty
attribute, you can use the following parameters:
Name
—. Enables you to specify the name of the attribute in the configuration file that corresponds to the property.
DefaultValue
—. Enables you to specify the default value of the property.
IsDefaultCollection
—. Enables you to specify whether the property represents the default collection of an element.
IsKey
—. Enables you to specify whether the property represents a key for a collection of configuration elements.
IsRequired
—. Enables you to specify whether this property must have a value.
Options
—. Enables you to use flags to specify the values of the above options.
You also can use validators when defining configuration properties. For example, in Listing 26.18, the RegexStringValidator
is used to check whether the value of the StyleSheetUrl
property ends with a .css
extension.
You can use the following validators with configuration properties:
CallbackValidator
—. Enables you to specify a custom method to use to validate a property value.
IntegerValidator
—. Enables you to validate whether a property value is an integer value (System.Int32
).
LongValidator
—. Enables you to validate whether a property value is a long value (System.Int64
).
PositiveTimeSpanValidator
—. Enables you to validate whether a property value is a valid time span.
RegexStringValidator
—. Enables you to validate a property value against a regular expression pattern.
StringValidator
—. Enables you to validate a property value that represents a string against a minimum length, maximum length, and list of invalid characters.
SubClassTypeValidator
—. Enables you to validate whether the value of a property is inherited from a particular class
TimeSpanValidator
—. Enables you to validate a property value that represents a time span against a minimum and maximum value.
When you use validators such as the RegexStringValidator
, make sure that you provide a property with a default value by using the DefaultValue
parameter with the ConfigurationProperty
attribute.
After you create a custom configuration section, you need to register it in a configuration file before you can use it. The web configuration file in Listing 26.19 adds the DesignSection
configuration section to the system.web
section.
Example 26.19. Web.config
<configuration> <configSections> <sectionGroup name="system.web"> <section name="design" type="AspNetUnleashed.DesignSection" allowLocation="true" allowDefinition="Everywhere"/> </sectionGroup> </configSections> <system.web> <design backcolor="red" styleSheetUrl="~/styles/style.css"/> </system.web> </configuration>
You are not required to add a custom configuration section to any particular configuration section group. For that matter, you are not required to add a custom configuration section to any configuration section group at all.
After you register a custom configuration section, you can use it just like any of the standard configuration sections. You can use the methods of the WebConfigurationManager
class to retrieve and modify the custom section.
For example, the page in Listing 26.20 uses the custom configuration section just created to retrieve the page background color and style sheet (see Figure 26.12).
Example 26.20. ShowDesignSection.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="AspNetUnleashed" %> <%@ Import Namespace="System.Web.Configuration" %> <%@ Import Namespace="System.Drawing" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Sub Page_Load() ' Get configuration Dim section As DesignSection = CType(WebConfigurationManager.GetWebApplicationSection("system.web/design"), DesignSection) ' Set Background Color htmlBody.Attributes("bgcolor") = ColorTranslator.ToHtml(section.BackColor) ' Set style sheet Dim link As New HtmlLink() link.Href = section.StyleSheetUrl link.Attributes.Add("rel", "stylesheet") link.Attributes.Add("type", "text/css") Page.Header.Controls.Add(link) End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show Design Section</title> </head> <body id="htmlBody" runat="server"> <form id="form1" runat="server"> <div> <h1>Custom Configuration Section Sample</h1> </div> </form> </body> </html>
A configuration element can contain a collection of child elements. For example, if you need to create a custom configuration section to configure a provider, then you use child elements to represent the list of providers.
The class in Listing 26.21 represents a configuration section for a ShoppingCart
. The configuration section includes three properties: MaximumItems
, DefaultProvider
, and Providers
. The Providers
property represents a collection of shopping cart providers.
Example 26.21. App_CodeShoppingCartSection.vb
Imports System Imports System.Configuration Namespace AspNetUnleashed Public Class ShoppingCartSection Inherits ConfigurationSection <ConfigurationProperty("maximumItems", DefaultValue:=100, IsRequired:=True)> _ Public Property MaximumItems() As Integer Get Return CType(Me("maximumItems"), Integer) End Get Set(ByVal Value As Integer) Me("maximumItems") = Value End Set End Property <ConfigurationProperty("defaultProvider")> _ Public Property DefaultProvider() As String Get Return CType(Me("defaultProvider"), String) End Get Set(ByVal Value As String) Me("defaultProvider") = value End Set End Property <ConfigurationProperty("providers", IsDefaultCollection:=False)> _ Public ReadOnly Property Providers() As ProviderSettingsCollection Get Return CType(Me("providers"), ProviderSettingsCollection) End Get End Property Public Sub New(ByVal maximumItems As Integer, ByVal defaultProvider As String) Me.MaximumItems = maximumItems Me.DefaultProvider = defaultProvider End Sub Public Sub New() End Sub End Class End Namespace
The Providers
property returns an instance of the ProviderSettingsCollection
class. This class is contained in the System.Configuration
namespace.
The web configuration file in Listing 26.22 illustrates how you can use the ShoppingCartSection
.
Example 26.22. Web.config
<configuration> <configSections> <sectionGroup name="system.web"> <section name="shoppingCart" type="AspNetUnleashed.ShoppingCartSection" allowLocation="true" allowDefinition="Everywhere" /> </sectionGroup> </configSections> <system.web> <shoppingCart maximumItems="50" defaultProvider="SqlShoppingCartProvider"> <providers> <add name="SqlShoppingCartProvider" type="AspNetUnleashed.SqlShoppingCartProvider" /> <add name="XmlShoppingCartProvider" type="AspNetUnleashed.XmlShoppingCartProvider" /> </providers> </shoppingCart> </system.web> </configuration>
The ShoppingCartSection
class takes advantage of an existing class in the .NET Framework: the ProviderSettingsCollection
class. If you have the need, you can create a custom configuration element collection class.
The AdminUsersSection
class in Listing 26.23 enables you to represent a list of users. The class includes a property named Users
that exposes an instance of the AdminUsersCollection
class. The AdminUsersCollection
represents a collection of configuration elements. The AdminUsersCollection
class is also defined in Listing 26.23.
Example 26.23. App_CodeAdminUsersSection.vb
Imports System Imports System.Configuration Namespace AspNetUnleashed Public Class AdminUsersSection Inherits ConfigurationSection <ConfigurationProperty("", IsDefaultCollection:=True)> _ Public ReadOnly Property Users() As AdminUsersCollection Get Return CType(Me(""), AdminUsersCollection) End Get End Property Public Sub New() End Sub End Class Public Class AdminUsersCollection Inherits ConfigurationElementCollection Protected Overrides Function CreateNewElement() As ConfigurationElement Return New AdminUser() End Function Protected Overrides Function GetElementKey(ByVal element As ConfigurationElement) As Object Return (CType(element, AdminUser)).Name End Function Public Sub New() Me.AddElementName = "user" End Sub End Class Public Class AdminUser Inherits ConfigurationElement <ConfigurationProperty("name", IsRequired:=True, IsKey:=True)> _ Public Property Name() As String Get Return CType(Me("name"), String) End Get Set(ByVal Value As String) Me("name") = Value End Set End Property <ConfigurationProperty("password", IsRequired:=True)> _ Public Property Password() As String Get Return CType(Me("password"), String) End Get Set(ByVal Value As String) Me("password") = Value End Set End Property End Class End Namespace
Notice that the ConfigurationProperty
attribute that decorates the Users
property sets the name of the configuration attribute to an empty string. It also marks the property as representing the section’s default collection. These options enable you to avoid having to create a subtag for the user collection. The user collection appears immediately below the main <adminUsers>
section tag.
The web configuration file in Listing 26.24 illustrates how you can use the AdminUsersSection
class.
Example 26.24. Web.config
<configuration> <configSections> <sectionGroup name="system.web"> <section name="adminUsers" type="AspNetUnleashed.AdminUsersSection" allowLocation="true" allowDefinition="Everywhere" /> </sectionGroup> </configSections> <system.web> <adminUsers> <user name="Bob" password="secret" /> <user name="Fred" password="secret" /> </adminUsers> </system.web> </configuration>
The ASP.NET page in Listing 26.25 displays all the users from the adminUsers
section in a BulletedList
control (see Figure 26.13).
Example 26.25. ShowAdminUsersSection.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="AspNetUnleashed" %> <%@ Import Namespace="System.Web.Configuration" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Sub Page_Load() ' Get configuration Dim section As AdminUsersSection = CType(WebConfigurationManager.GetWebApplicationSection("system.web/adminUsers"), AdminUsersSection) ' Bind section to GridView bltAdminUsers.DataSource = section.Users bltAdminUsers.DataBind() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Show AdminUsersSection</title> </head> <body> <form id="form1" runat="server"> <div> <h1>Administrators</h1> <asp:BulletedList id="bltAdminUsers" DataTextField="Name" Runat="server" /> </div> </form> </body> </html>
If you need to protect sensitive information stored in a configuration file, you can encrypt the information. For example, you should always encrypt the connectionStrings
section of a configuration file to prevent your database connection strings from being stolen by evil hackers.
You can encrypt just about any section in the web configuration file. You can encrypt any of the sections in the system.web
section group with the sole exception of the processModel
section. You also can encrypt a custom configuration section.
The .NET Framework uses the Provider Model for encrypting configuration sections. The Framework ships with two ProtectedConfigurationProviders
: the RsaProtectedConfigurationProvider
and the DpapiProtectedConfigurationProvider
.
The RsaProtectedConfigurationProvider
protect sensitive information stored in a configuration file, you can encrypt is the default provider. It uses the RSA algorithm to protect a configuration section. The RSA algorithm uses public key cryptography. It depends on the fact that no one has discovered an efficient method to factor large prime numbers.
The second provider, the DpapiProtectedConfigurationProvider
, uses the Data Protection API (DPAPI) to encrypt a configuration section. The DPAPI is built into the Windows operating system (Microsoft Windows 2000 and later). It uses either Triple-DES or AES (the United States Government–standard encryption algorithm) to encrypt data.
The RsaProtectedConfigurationProvider
is the default provider, and it is the one that you should almost always use. The advantage of the RsaProtectedConfigurationProvider
is that this provider supports exporting and importing encryption keys. This means that you can move an application that contains an encrypted configuration file from one web server a new web server. For example, you can encrypt a configuration section on your development web server and deploy the application to a production server.
If you use the DpapiProtectedConfigurationProvider
to encrypt a configuration section, on the other hand, then you cannot decrypt the configuration section on another web server. If you need to move the configuration file from one server to another, then you need to first decrypt the configuration file on the source server and re-encrypt the configuration file on the destination server.
The .NET Framework uses the World Wide Web Consortium (W3C) recommendation for encrypting XML files. This recommendation is located at http://www.w3.org/TR/2002/REC-xmlenc-core-20021210/.
You can use encryption not only with configuration files, but also with other XML files. To learn more about encrypting XML files, look up the EncryptedXml
class in the Microsoft .NET Framework 2.0 SDK Documentation.
The easiest way to encrypt a section in the web configuration file is to use the aspnet_regiis
command-line tool. This tool is located at the following path:
WINDOWSMicrosoft.NETFramework[version]aspnet_regiis.exe
You don’t need to navigate to the Microsoft.NET directory to execute the aspnet_regiis
tool if you open the SDK Command Prompt.
If you want to encrypt a particular section of a configuration file, then you can use the -pef
option when executing the aspnet_regiis
tool. For example, the following command encrypts the connectionStrings
section of a configuration file located in a folder named MyWebApp:
aspnet_regiis -pef connectionStrings c:WebsitesMyWebApp
If you prefer, rather than specify the location of a web application by its file system path, you can use its virtual path. The following command encrypts the connectionStrings
section of a configuration file located in a virtual directory named /MyApp:
aspnet_regiis -pe connectionStrings -app /MyApp
Notice that the -app
option is used to specify the application’s virtual path.
You can decrypt a configuration section by using the -pdf
option. The following command decrypts a configuration file located in a folder named MyWebApp:
aspnet_regiis -pdf connectionStrings c:WebsitesMyWebApp
You also can decrypt a configuration section by specifying a virtual directory. The following command uses the -pd
option with the -app
option:
aspnet_regiis -pd connectionStrings -app /MyApp
When you encrypt a configuration section, you can specify the ProtectedConfigurationProvider
to use to encrypt the section. The Machine.config
file configures two providers: the RsaProtectedConfigurationProvider
and the DataProtectionConfigurationProvider
. The RsaProtectedConfigurationProvider
is used by default.
If you execute the following command, then the connectionStrings
section is encrypted with the DataProtectionConfigurationProvider
:
aspnet_regiis -pe connectionStrings -app /MyApp -prov ProtectedConfigurationProvider
Notice that this command includes a -prov
option that enables you to specify the ProtectedConfigurationProvider
.
Instead of using the aspnet_regiis
tool to encrypt configuration sections, you can use the Configuration API. Specifically, you can encrypt a configuration section by calling the SectionInformation.ProtectSection()
method.
For example, the ASP.NET page in Listing 26.26 displays all the sections contained in the system.web
section group in a GridView
control. You can click Protect to encrypt a section, and you can click UnProtect to decrypt a section (see Figure 26.14).
Example 26.26. EncryptConfig.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.Web.Configuration" %> <%@ Import Namespace="System.Collections.Generic" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> Private Sub Page_Load() If Not Page.IsPostBack Then BindSections() End If End Sub Protected Sub grdSections_RowCommand(ByVal sender As Object, ByVal e As GridViewCommandEventArgs) Dim rowIndex As Integer = Int32.Parse(CType(e.CommandArgument, String)) Dim sectionName As String = CType(grdSections.DataKeys(rowIndex).Value, String) If e.CommandName = "Protect" Then ProtectSection(sectionName) End If If e.CommandName = "UnProtect" Then UnProtectSection(sectionName) End If BindSections() End Sub Private Sub ProtectSection(ByVal sectionName As String) Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath) Dim section As ConfigurationSection = config.GetSection(sectionName) section.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider") config.Save(ConfigurationSaveMode.Modified) End Sub Private Sub UnProtectSection(ByVal sectionName As String) Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath) Dim section As ConfigurationSection = config.GetSection(sectionName) section.SectionInformation.UnprotectSection() config.Save(ConfigurationSaveMode.Modified) End Sub Private Sub BindSections() Dim config As Configuration = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath) Dim colSections As New List(Of SectionInformation)() For Each section As ConfigurationSection In config.SectionGroups("system.web").Sections colSections.Add(section.SectionInformation) Next grdSections.DataSource = colSections grdSections.DataBind() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Encrypt Config</title> </head> <body> <form id="form1" runat="server"> <div> <asp:GridView id="grdSections" DataKeyNames="SectionName" AutoGenerateColumns="false" OnRowCommand="grdSections_RowCommand" Runat="server" > <Columns> <asp:ButtonField ButtonType="Link" Text="Protect" CommandName="Protect" /> <asp:ButtonField ButtonType="Link" Text="UnProtect" CommandName="UnProtect" /> <asp:CheckBoxField DataField="IsProtected" HeaderText="Protected" /> <asp:BoundField DataField="SectionName" HeaderText="Section" /> </Columns> </asp:GridView> </div> </form> </body> </html>
When you click the Protect
link, the grdSection_RowCommand()
event handler executes and calls the ProtectSection()
method. This method calls the SectionInformation.ProtectSection()
method to encrypt the selected section. Notice that the name of a ProtectedConfigurationProvider
is passed to the ProtectSection()
method.
The page in Listing 26.26 saves the configuration file. By default, the ASPNET
and NETWORK SERVICE
accounts do not have permission to write to the file system. If you want the page in Listing 26.26 to execute within the security context of the user requesting the page, then you can enable per-request impersonation by adding the configuration file in Listing 26.16 to the root of your application.
If you need to copy an encrypted configuration file from one server to a new server, then you must copy the keys used to encrypt the configuration file to the new server. Otherwise, your application can’t read encrypted sections of the configuration file on the new server.
You can’t copy an encrypted configuration file from one server to another when you are using the DpapiProtectedConfigurationProvider
. This section assumes that you are using the RsaProtectedConfigurationProvider
.
By default, the RsaProtectedConfigurationProvider
uses a public/private key pair stored in a key container named NetFrameworkConfigurationKey
. This key container is located at the following path:
Documents and SettingsAll UsersApplication DataMicrosoftCryptoRSAMachineKeys
If you want to deploy an application that contains an encrypted configuration file to a new server, then you must configure a new key container and import the key container to the new server. You must complete five configuration steps:
Create a new key container.
Configure your application to use the new key container.
Export the keys from the origin server.
Import the keys on the destination server.
Grant access to the key container to your ASP.NET application.
You need to perform this sequence of configuration steps only once. After you have set up both servers to use the same encryption keys, you can copy ASP.NET applications back and forth between the two servers and read the encrypted configuration sections. Let’s examine each of these steps one by one.
First, you need to create a new key container because the default key container, the NetFrameworkConfigurationKey
key container, does not support exporting both the public and private encryption keys. Execute the following command from a command prompt:
aspnet_regiis -pc "SharedKeys" -exp
This command creates a new key container named SharedKeys
. The -exp
option is used to make any keys added to the container exportable.
After you create the new key container, you must configure your application to use it. The web configuration file in Listing 26.27 configures the RsaProtectedConfigurationProvider
to use the SharedKeys
key container.
Example 26.27. Web.config
<?xml version="1.0"?> <configuration> <configProtectedData defaultProvider="MyProtectedConfigurationProvider"> <providers> <add name="MyProtectedConfigurationProvider" type="System.Configuration.RsaProtectedConfigurationProvider" cspProviderName="" useMachineContainer="true" useOAEP="false" keyContainerName="SharedKeys" /> </providers> </configProtectedData> <connectionStrings> <add name="Movies" connectionString="Data Source=DataServer;Integrated Security=true; Initial Catalog=MyDB" /> </connectionStrings> </configuration>
Notice that the configuration file in Listing 26.27 includes a configProtectedData
section. This section is used to configure a new ProtectedConfigurationProvider
named MyProtectedConfigurationProvider
. This provider includes a keyContainerName
attribute that points to the SharedKeys
key container.
The next step is to export the keys contained in the SharedKeys
key container to an XML file. You can export the contents of the SharedKeys
key container by executing the following command:
aspnet_regiis -px "SharedKeys" keys.xml -pri
Executing this command creates a new XML file named keys.xml
. The -pri
option causes both the private and public key—and not only the public key—to be exported to the XML file.
The XML key file contains very secret information (the keys to the kingdom). After importing the XML file, you should immediately destroy the XML file (or stick the XML file on a CD and lock the CD away in a safe location).
After you create the keys.xml
file on the origin server, you need to copy the file to the destination server and import the encryption keys. Execute the following command on the destination server to create a new key container and import the encryption keys:
aspnet_regiis -pi "SharedKeys" keys.xml
The final step is to grant access to the key container to your ASP.NET application. By default, a page served from Internet Information Server executes within the security context of either the NETWORK SERVICE
account (Windows 2003 Server) or the ASPNET
account (other operating systems). You can grant access to the SharedKeys
key container to the ASPNET
account by executing the following command:
aspnet_regiis -pa "SharedKeys" "ASPNET"
Executing this command modifies the ACLs for the SharedKeys
key container so that the ASPNET
account has access to the encryption keys.
After you complete this final step, you can transfer ASP.NET applications with encrypted configuration files back and forth between the two servers. An application on one server can read configuration files that were encrypted on the other server.
This chapter was devoted to the topic of configuration. In the first section, you were provided with an overview of the configuration sections used by the ASP.NET Framework. You learned how to lock configuration sections to prevent sections from being modified. You also learned how to place configuration sections in external files.
Next, we tackled the topic of the Configuration API. You learned how to read and modify configuration files programmatically. You also learned how to provision new ASP.NET applications by creating new virtual directories and configuration files.
You also learned how to create custom configuration sections. You learned how to create both simple custom configuration sections and custom configuration sections that contain custom collections of configuration elements.
Finally, we discussed the topic of encryption. You learned how to encrypt a configuration section by using the aspnet_regiis
command-line tool. You also learned how to encrypt configuration sections programmatically. In the final section, you also learned how to deploy encrypted configuration files from a development server to a production server.