Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]

.pdf
Скачиваний:
107
Добавлен:
16.08.2013
Размер:
29.8 Mб
Скачать

788 C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

In both cases, the tool adds a little configuration entry to the application’s web.config file. You can do this manually, just as you can enable the Roles Service.

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <system.web>

<roleManager enabled="true" /> <authentication mode="Forms" />

</system.web>

</configuration>

With this configuration in place, ASP.NET automatically creates a file-based database, ASPNETDB.MDF, in the application’s App_Data directory, as already described in Chapter 21. If you want to use a custom store, you have to complete the following steps:

1.Create the data store either by using aspnet_regsql.exe or by executing the TSQL command scripts included in the .NET Framework directory. Both were introduced in Chapter 21.

2.Configure the Roles provider to use the previously created custom store.

You can configure the Roles provider through the <roleManager> tag. You can either use a different database or use a completely different store if you want. In addition, you can configure certain properties through the <roleManager> tag that can’t be configured in the WAT.

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <connectionStrings>

<add name="MySqlStore" connectionString="data source=(local);

Integrated Security=SSPI;initial catalog=MySqlDB"/> </connectionStrings>

<system.web>

<roleManager enabled="true" defaultProvider="MySqlProvider" cacheRolesInCookie="true" cookieName=".MyRolesCookie" cookieTimeout="30" cookieSlidingExpiration="true" cookieProtection="All">

<providers>

<add name="MySqlProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="MySqlStore" applicationName="RolesDemo"/>

</providers>

</roleManager> <authentication mode="Forms"/> <compilation debug="true"/>

</system.web>

</configuration>

As soon as you have added this configuration entry to your web.config file, you can select the provider through the WAT. Just switch to the Provider tab, and then click the link Select a Different Provider for Each Feature. Figure 23-3 shows the provider selection in the WAT.

C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

789

Figure 23-3. The Roles provider in the web-based configuration tool

Table 23-1 lists the properties you can configure through the <roleManager> configuration tag.

Table 23-1. Options for the <roleManager> Configuration

Option

Description

Enabled

Indicates whether the Roles Service is enabled (true) or not (false).

defaultProvider

Optional attribute for specifying the currently active provider for

 

storing role information. If you want to use a different provider, you

 

have to configure it and set the defaultProvider attribute to the name

 

of the provider you want to use.

cacheRolesInCookie

Instead of reading the roles every time from the back-end store, you

 

can store roles in a cookie. This attribute indicates whether a cookie

 

is used.

cookieName

If roles are cached in a cookie, you can specify a name for this cookie

 

through this attribute.

cookiePath

Specifies the root path of the cookie. The default value is /.

cookieProtection

The roles cookie can be encrypted and signed. The level of protection

 

is specified through this attribute. Valid values are All (encrypt and

 

sign), Encryption, Validation, and None.

Continued

790 C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

Table 23-1. Continued

Option

Description

cookieRequireSSL

Specifies whether the cookie will be returned by ASP.NET only if SSL is

 

enabled (true) or in any other case (false). If this attribute is set to true

 

and SSL is not activated, the runtime simply doesn’t return the cookie,

 

and therefore role checks always happen against the underlying Roles

 

provider.

cookieTimeout

Gets or sets a timeout for the roles cookie in minutes with a default of

 

30 minutes.

createPersistentCookie

If set to true, the cookie will be stored persistently on the client

 

machine. Otherwise, the cookie is just a session cookie that will be

 

deleted when the user is closing the browser.

domain

Specifies the valid domain for the role cookie.

maxCachedResults

Specifies the maximum number of role names persisted in the cookie.

 

 

In the previous example, you configured the SqlRoleProvider. The provider includes a couple of additional settings you can configure through web.config, as shown in Table 23-2.

Table 23-2. Additional Properties of the SqlRoleProvider

Property

Description

name

Name of the provider. This name can be used in the defaultProvider

 

attribute described in Table 23-1 for specifying the provider by the

 

application.

applicationName

Name of the application for which the roles are managed.

description

Short, friendly description of the provider.

connectionStringName

Name of the connection string specified in the web.config file’s

 

<connectionStrings> section that will be used for connecting to the

 

back-end roles store.

 

 

In addition to the SqlRoleProvider, ASP.NET ships with a provider that can be used on Windows Server 2003 with Authorization Manager. You can also create and use your own custom providers, as you will learn in Chapter 26. Table 23-3 shows the classes included in the Roles Service framework.

Table 23-3. The Fundamental Roles Service Classes

Class

Description

RoleManagerModule

This module ensures that roles will be assigned to the currently

 

logged-on user for every request. It attaches to the Application_

 

AuthenticateRequest event and creates an instance of RolePrincipal

 

containing the roles the user is assigned to automatically if the Roles

 

Service is enabled in web.config.

RoleProvider

Base class for every Roles provider that defines the interface you must

 

implement for a custom RoleProvider. Every custom provider must be

 

inherited from this class.

RoleProviderCollection

A collection of Roles providers. This collection allows you to iterate

 

through the configured Roles providers on your system and for your

 

application.

SqlRoleProvider

Implementation of a Roles provider for SQL Server–based databases.

C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

791

Class

Description

WindowsTokenRoleProvider

Gets role information for an authenticated Windows user

 

based on Windows group associations.

AuthorizationStoreRoleProvider

Implementation of a Roles provider for storing roles in a

 

Authorization Manager–based store. Authorization Manager

 

ships with Windows Server 2003 and allows you to declara-

 

tively define application roles and permissions for this role.

 

Your application can use Authorization Manager for

 

programmatically authorizing users.

Roles

You use the Roles class as your primary interface to the roles

 

store. This class includes methods for programmatically

 

managing roles.

RolePrincipal

This is a IPrincipal implementation that connects the

 

configured roles with the authenticated user. It is created

 

automatically by the RoleManagerModule if the Roles

 

Service is enabled.

 

 

As soon as you have configured the Roles Service, you can create users and roles and then assign users to these roles using either the WAT or the Roles class in your code. On the Security tab, just click the Create or Manage Roles link. Then you can create roles and add users to roles, as shown in Figure 23-4.

Figure 23-4. Adding users to roles

After you have configured users and roles, you need to configure the authorization rules for your application. You have already learned all the necessary details. Just configure the appropriate <authorization> sections in the different directories of your application. Fortunately, you even don’t have to do this manually. When selecting the Security tab, you just need to click one of the links in the Access Rules section, as shown in Figure 23-5.

792 C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

Figure 23-5. Configuring access rules with the WAT

When the Roles Service is enabled, the RoleManagerModule automatically creates a RolePrincipal instance containing both the authenticated user’s identity and the roles of the user. The RolePrincipal is just a custom implementation of IPrincipal, which is the base interface for all principal classes. It therefore supports the default functionality, such as access to the authenticated identity and a method for verifying a role membership condition through the IsInRole() method. Furthermore, it employs a couple of additional properties for accessing more detailed information about the principal. You can use the properties in the following code for extracting information from the instance as well as for performing authorization checks by calling the IsInRole() method:

protected void Page_Load(object sender, EventArgs e)

{

if (User.Identity.IsAuthenticated)

{

RolePrincipal rp = (RolePrincipal)User;

StringBuilder RoleInfo = new StringBuilder();

RoleInfo.AppendFormat("<h2>Welcome {0}</h2>", rp.Identity.Name);

RoleInfo.AppendFormat("<b>Provider:</b> {0}<BR>", rp.ProviderName);

RoleInfo.AppendFormat("<b>Version:</b> {0}<BR>", rp.Version);

RoleInfo.AppendFormat("<b>Expires at:</b> {0}<BR>", rp.ExpireDate);

RoleInfo.Append("<b>Roles:</b> ");

string[] roles = rp.GetRoles();

for (int i = 0; i < roles.Length; i++)

{

if (i > 0) RoleInfo.Append(", ");

C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

793

RoleInfo.Append(roles[i]);

}

LabelRoleInformation.Text = RoleInfo.ToString();

}

}

Using the LoginView Control with Roles

In the previous chapter, you learned details about the security controls that ship with ASP.NET. One of these controls is the LoginView control. You used this control in Chapter 21 for displaying different controls for anonymous and logged-in users. The control uses templates for implementing this functionality. In Chapter 21 you used the <LoggedInTemplate> and <AnonymousTemplate> templates.

The control supports one additional template that enables you to create different views based on the roles to which a user belongs. For this purpose you need to add a RoleGroups template with <asp:RoleGroup> controls. Within every <asp:RoleGroup> control, you specify a comma-separated list of roles in the Roles attribute for which its <ContentTemplate> will be displayed, as follows:

<asp:LoginView runat="server" ID="MainView"> <LoggedInTemplate>

<h2>This is the logged in template</h2> </LoggedInTemplate>

<RoleGroups>

<asp:RoleGroup Roles="Admin"> <ContentTemplate>

<h2>Only Admins will see this</h2> </ContentTemplate>

</asp:RoleGroup>

<asp:RoleGroup Roles="Contributor"> <ContentTemplate>

<h2>This is for contributors!</h2> </ContentTemplate>

</asp:RoleGroup>

<asp:RoleGroup Roles="Reader, Designer"> <ContentTemplate>

<h2>This is for web designers and readers</h2> </ContentTemplate>

</asp:RoleGroup>

</RoleGroups>

</asp:LoginView>

The LoginView control in the previous code displays different content for logged-in users and for users assigned to specific roles. For example, for users in the Admin role the control displays the text “Only Admins will see this,” while for users in the Contributor role it displays the text “This is for contributors.” Also, for users who are associated with the Reader or Designer role, it displays different content.

It’s important to understand that just one of these templates will be displayed. The control simply displays the first template that fits the logged-in user. For example, if you have a user associated with the Contributor, Reader, and Designer roles, the first matching template is

the <asp:RoleGroup> for contributors. The other role group will simply not be displayed. The LoggedInTemplate, for example, will be displayed only for authenticated users with no matching <asp:RoleGroup> element. As soon as a matching role group is found, the contents of the LoggedInTemplate will not be displayed.

794 C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

Accessing Roles Programmatically

As is the case for the Membership API introduced in Chapter 21, the Roles Service includes an API that allows you to perform all tasks from code. You can programmatically add new roles, read role information, and delete roles from your application. Furthermore, you can associate users with roles as well as get users associated with a specific role. You can do all this by calling methods of the Roles class.

Most of the properties included in the Roles class just map to the settings for the <roleManager> tag described in Table 23-1. Therefore, Table 23-4 includes the additional properties and the Roles class’s methods that you can use for managing and accessing the Roles Service programmatically.

Table 23-4. Members of the Roles Class

Member

Description

Provider

Returns the provider currently used by your application.

Providers

Returns a collection of all the available providers on the system and

 

for your application. It therefore returns the providers configured in

 

machine.config and in web.config of your application.

AddUserToRole

Accepts a user name and a role name as a string parameter and adds

 

the specified user to the specified role.

AddUserToRoles

Accepts a user name as a string parameter and role names as an

 

array of strings and adds the specified user to all the roles specified

 

in the role names parameter.

AddUsersToRole

Accepts a string array with user names and a string parameter that

 

specifies a role name and adds all the specified users to the role

 

specified in the second parameter.

AddUsersToRoles

Accepts a string array with user names and a second one with role

 

names and adds all the users in the user names parameter to all the

 

roles in the role names parameter.

CreateRole

Creates a new role.

DeleteRole

Deletes an existing role.

FindUsersInRole

Accepts a string array with a list of role names and a string

 

parameter with a list of user names. It returns every user specified

 

in the user names array that is associated with one of the roles

 

specified in the array of role names.

GetAllRoles

Returns a string array containing all the role names of the roles

 

available in the role store of the configured provider.

GetRolesForUser

Returns a string array containing all the roles the specified user is

 

associated with.

GetUsersInRole

Returns a list of users who are associated with the role passed in as a

 

parameter.

IsUserInRole

Returns true if the specified user is a member of the specified role.

RemoveUserFromRole

Removes a single user from the specified role.

RemoveUserFromRoles

Removes the specified user from all roles specified.

RemoveUsersFromRole

Removes all the specified users from a single role.

RemoveUsersFromRoles

Removes all the specified users from all the specified roles.

RoleExists

Returns true if a role exists and otherwise false.

 

 

C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

795

A good use for accessing roles programmatically is to associate users to roles automatically when they register themselves. Of course, this is useful only for specific roles. Imagine that your application supports a role called Everyone, and every single user should be a member of this role. If you register users on your own, you can enter this relationship manually. But if your application supports self-registration for Internet users, you can’t do this. Therefore, you somehow have to make sure users will be associated with the Everyone role automatically.

With your first attempt, you might want to catch the CreatedUser event of the CreateUserWizard control, but that’s not sufficient. Remember the existence of the ASP.NET WAT, where you can create users. In this case, catching the CreatedUser event of the control placed in your application won’t help. Therefore, you have to find a different solution. You definitely need an applicationwide event for this purpose, although this will not be raised by the configuration application because it is a different application. One possibility is to catch the Application_AuthenticateRequest event; within the event you verify whether the user is a member of the Everyone class. If not, you can add the user automatically. This shifts the task of adding a user automatically to the role to the point of authentication, which definitely affects every user. To do so, you just have to add a global application class to your project and add the following code.

Caution Of course, you should do something like this only for the lowest privileged roles such as Everyone. It’s never a good idea to perform such an action for any other type of role.

void Application_AuthenticateRequest(Object sender, EventArgs e)

{

if (User != null)

{

if (User.Identity.IsAuthenticated && Roles.Enabled)

{

string EveryoneRoleName = ConfigurationManager.AppSettings["EveryoneRoleName"];

if (!Roles.IsUserInRole(EveryoneRoleName) && Roles.RoleExists(EveryoneRoleName))

{

Roles.AddUserToRole(User.Identity.Name, EveryoneRoleName);

}

}

}

}

The previous code reads the name of the Everyone role from the configuration file so that it is not hard-coded into the application. It then uses the Roles class to check whether the user is already associated with the role, and if not, it checks whether the role exists. If the user is not associated with the role, and the user exists in the system, it uses the Roles.AddUsersToRole method for programmatically adding the user to the Everyone role.

Caution You might want to use the User.IsInRole() in the previous code; however, this is not valid. When the application-wide Application_AuthenticateRequest is called, the RoleManagerModule itself has not been called yet. Therefore, the RolePrincipal with the association of the user and its roles has not been created yet, so a call such as User.IsInRole("Everyone") would return false. Later in your page code—for example, in a Page_Load routine— the RolePrincipal is already initialized, and the call to User.IsInRole("Everyone") will work appropriately.

796 C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

Using the Roles Service with Windows Authentication

The Roles Service comes with a provider that integrates with Windows roles for Windows authentication: the WindowsTokenRoleProvider. This provider retrieves the Windows group membership information for the currently logged-on user and provides it in the same way for your application as you saw previously with the SqlRoleProvider. When using the WindowsTokenRoleProvider, you have to configure your application using Windows authentication and then configure the WindowsTokenRoleProvider as follows:

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <system.web>

<authentication mode="Windows"/>

<authorization>

<deny users="?" /> </authorization> <roleManager enabled="true"

cacheRolesInCookie="false"

defaultProvider="WindowsRoles">

<providers>

<add name="WindowsRoles" type="System.Web.Security.WindowsTokenRoleProvider" />

</providers>

</roleManager>

</system.web>

</configuration>

With this configuration in place, the user is authenticated through Windows authentication. The RoleManagerModule automatically creates an instance of RolePrincipal and associates it with the HttpContext.Current.User property. Therefore, you can use the RolePrincipal as follows—there is no difference compared to other Roles providers in terms of usage:

protected void Page_Load(object sender, EventArgs e)

{

if ((User != null) && (User.Identity.IsAuthenticated))

{

RolePrincipal rp = (RolePrincipal)User;

StringBuilder Info = new StringBuilder();

Info.AppendFormat("<h2>Welcome {0}!</h2>", User.Identity.Name);

Info.AppendFormat("<b>Provider: </b>{0}<br>", rp.ProviderName);

Info.AppendFormat("<b>Version: </b>{0}<br>", rp.Version);

Info.AppendFormat("<b>Expiration: </b>{0}<br>", rp.ExpireDate);

Info.AppendFormat("<b>Roles: </b><br>");

string[] Roles = rp.GetRoles(); foreach (string role in Roles)

{

if (!role.Equals(string.Empty)) Info.AppendFormat("-) {0}<br>", role);

}

LabelPrincipalInfo.Text = Info.ToString();

}

}

You can see the result of the previous code in Figure 23-6.

C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

797

Figure 23-6. Results of querying the RolePrincipal with Windows authentication

The provider-based architecture enables you to use Windows authentication with Windows groups without changing the inner logic of your application. Everything works the same as with the SqlRoleProvider. The same is true for the Membership API introduced in Chapter 21. When configuring another provider, you don’t have to change your code; however, you should have some programmatic authorization checks with hard-coded role names in your code, because the Windows groups include the domain qualifier and the custom roles do not. To avoid this, you can add functionality to your application that allows you to associate roles with permissions in either a database or a configuration file. The way you do this depends on the requirements of your application.

We suggest not using Windows groups for authorization in your application directly except for a few of the built-in groups such as the Administrators group. In most cases, it’s useful to define roles that are specific to your application. This is why:

Windows groups other than the built-in groups depend on the name of the domain or machine on which they exist.

In most cases, Windows groups in a domain are structured according to the organizational and network management requirements of the enterprise. Often these requirements do not map to the application requirements.

Structuring application roles independently from the network groups makes your application more flexible and usable across multiple types of network structures.

A good example that introduces such a design is Windows SharePoint Services. SharePoint is a ready-to-use portal solution built on ASP.NET 1.x that can be used for free with Windows Server 2003. SharePoint includes prebuilt functionality for document libraries, meeting workspaces, and lists. You can use SharePoint for collaboratively working in teams—sharing documents, planning meetings and more.