Chapter 12. Authenticating Users

In this chapter, you learn how to add security to your ASP.NET MVC application. You learn how to password protect controllers and controller actions. You also learn how to register new users.

We also investigate how you can configure different types of authentication and Membership to use with an ASP.NET MVC application. For example, you learn how to authenticate users using Windows authentication and Forms authentication.

Finally, you learn how to create tests for controller actions that use authentication. You learn how to verify that only the right users can invoke a controller action.

Creating Users and Roles

Before we do anything else, we should start by creating some users and roles. We can use these users and roles in the remainder of the chapter to verify that we provide access to only the right users in the right roles. There are two ways that we can create new users: We can use the Web Site Administration Tool or use the Account controller.

Using the Web Site Administration Tool

You can take advantage of the Web Site Administration Tool to quickly create new users and roles. You can launch the Web Site Administration Tool from Visual Studio by selecting the menu option Project, ASP.NET Configuration.

Alternatively, you can click the scary icon of the hammer hitting the world that appears at the top of the Solution Explorer window (see Figure 12.1).

Figure 12.1. Launching the Web Site Administration Tool

image

Warning

Before launching the Web Site Administration Tool, make sure that you have built your application at least once. Otherwise, you get the error message Could Not Load Type ’MvcApplication1.MvcApplication’ when you click the Security tab.

The Web Site Administration Tool is implemented as an ASP.NET Application. If you are curious, you can take a look at the source for this application in the following folder:

C:WindowsMicrosoft.NETFrameworkv2.0.50727ASP.NETWebAdminFiles

After you launch the Web Site Administration Tool, you can create new users and roles by clicking the Security tab. For example, I created two new users named Jack and Jill that I used to test the sample code in this chapter. (They both have the password secret.)

You also can enable roles and create roles. I created a role named Manager and added Jill to the role (see Figure 12.2).

Figure 12.2. Adding Jill to the Manager role

image

Note

You can’t run the Web Site Administration over the web (remotely).

Using the Account Controller

If you need to create users (but not roles), you can use the Account controller included with the default Visual Studio ASP.NET MVC template. Follow these steps:

  1. Create a new ASP.NET MVC application and run it. (Press F5.)
  2. Click the [Log On] link (see Figure 12.3).

    Figure 12.3. Click the Log On link

    image
  3. Click the Register link.
  4. Complete the registration form to create a new user (see Figure 12.4).

    Figure 12.4. Registering a new user

    image

The Account controller is located in the Controllers folder just like any other controller. The Account controller exposes the following actions:

LogOn()—Enables you to display a LogOn view

LogOn(userName, password, rememberMe, returnUrl)—Enables you to authenticate

LogOff()—Enables you to log off from the website

Register()—Enables you to display the registration form

Register(userName, email, password, confirmPassword)—Enables you to register a new user account

ChangePassword()—Enables you to display the ChangePassword view

ChangePassword(currentPassword, newPassword, confirmPassword)—Enables you to change a password

ChangePasswordSuccess()—Enables you to display the ChangePasswordSuccess view

Notice that the Account controller exposes actions for changing a password. The sample ASP.NET MVC site does not provide you with a way to navigate to the ChangePassword view. (There is no link to this view.) You can get to the ChangePassword view by entering the following address into your browser address bar:

/Account/ChangePassword

You must be authenticated to navigate to the ChangePassword view. After you log on, you can use the ChangePassword view to change your password (see Figure 12.5).

Figure 12.5. Changing your password

image

Authorizing Users

If you want to allow only certain users, or only users in certain roles, to access a controller action, you need to specify the users or roles that are authorized to invoke the controller action. There are two ways that you can specify authorization: You can specify authorization declaratively, or you can specify authorization programmatically. We explore both approaches in the following sections.

Using the Authorize Attribute

You can decorate a controller action, or an entire controller class, with the Authorize attribute. If you use this attribute without specifying any additional properties, the attribute prevents any anonymous users from invoking the action.

For example, the Secrets() action in Listing 12.1 is decorated with the Authorize attribute.

Listing 12.1. ControllersCompanyController.cs (Secret) (C#)

image


Listing 12.1. ControllersCompanyController.vb (Secret) (VB)

image


If you attempt to invoke the Secrets() action, and you are not logged in, you are redirected to the Account controller Login() action automatically. This action displays the Login view.

If, on the other hand, you are logged in, you can see the Secrets view in Figure 12.6.

Figure 12.6. Company Secrets

image

Authorizing Particular Users

If you want to authorize only certain users to invoke a controller action, you can use the Users property when applying the Authorize attribute to an action. The Users property accepts a comma-separate list of users.

For example, the SuperSecrets() action in Listing 12.2 can be invoked only by Jack or Jill. If Jim attempts to invoke this action, he will be redirected to the LogOn view.

Listing 12.2. ControllersCompanyController.cs (SuperSecrets) (C#)

image


Listing 12.2. ControllersCompanyController.vb (SuperSecrets) (VB)

image


If either Jack or Jill invoke the SuperSecrets() action, they see the view in Figure 12.7.

Figure 12.7. Super Secret World Domination Plans

image

Authorizing Particular Roles

You also can restrict access to a controller action by role. For example, you might want to enable only users in the Manager role to invoke a controller action.

The Authorize attribute includes a Roles property. You can assign a comma-separate list of roles to this attribute. For example, the SuperSuperSecret() controller action in Listing 12.3 can be invoked only by members of the Manager role.

Listing 12.3. ControllersCompanyController.cs (SuperSuperSecret) (C#)

image


Listing 12.3. ControllersCompanyController.vb (SuperSuperSecret) (VB)

image


Because Jill is a member of the Manager role, she can successfully invoke the SuperSuperSecrets() action (see Figure 12.8). However, when anyone who is not a member of the Manager role attempts to invoke the action, that person is redirected to the Account controller LogOn() action.

Figure 12.8. Super Super Secret Plans for World Domination

image

Using the User Property

If you prefer, you can implement authorization programmatically rather than declaratively. Instead of using the Authorize attribute, you can use the User property.

The User property is a property of the Controller class. The User property implements the IPrincipal interface and, therefore, it exposes the following method and property:

IsInRole(role)—Returns true if the current user is a member of the specified role

Identity—Return an object that implements the IIdentity interface that represents the current user

The expression User.Identity returns an object that represents the current user. The User.Identity object has three properties:

AuthenticationType—Returns a string that represents the authentication type such as Forms or NTLM. This property returns an empty string when the user is not authenticated.

IsAuthenticated—Returns true when the user is authenticated.

Name—Returns the username.

The controller in Listing 12.4 illustrates how you can use these methods and properties. The Secrets() action returns different views to different users.

Listing 12.4. ControllersUserController.cs (C#)

image


Listing 12.4. ControllersUserController.vb (VB)

image


If you invoke the Index() action, and you are a member of the Manager role, you see the ManagerView. If your username happens to be Jack, you see the JackView. If you are an authenticated user, you see the AuthenticatedView. Finally, if you are an anonymous user, you are redirected to the Account controller LogOn action so that you can log in to the website.

Configuring Membership

In this section, you learn how to configure membership. For example, you learn how to specify where user account information is stored. You also learn how to configure membership with different password requirements.

Configuring the Membership Database

You might be curious about where user account information is stored. By default, user names and passwords are stored in a SQL Express database named ASPNETDB.mdf located in your application’s App_Data folder. You can see this database in the Solution Explorer window if you click the Show All Files button at the top of the Solution Explorer window (see Figure 12.9).

Figure 12.9. The ASPNETDB.mdf database

image

Using the ASPNETDB.mdf database is fine (and convenient) when you develop a new application. However, when you are ready to release the application into production, you need to store your user information in your production database.

Note

You can use ASP.NET membership with Microsoft SQL Server version 2000 or above. You can’t use membership with another type of database—for example, an Oracle database—without creating a custom membership provider.

If you want to store user account information in an alternative database, you need to complete the following three steps:

  1. Configure your database.
  2. Configure your application.
  3. Configure database permissions.

You can complete the first step, configuring your database, by using the aspnet_regsql.exe command line tool. From the Start menu, navigate to All Programs, Microsoft Visual Studio 2008, Visual Studio Tools, Visual Studio 2008 Command Prompt. Type aspnet_regsql.exe into the command prompt to launch the ASP.NET SQL Server Setup Wizard (see Figure 12.10).

Figure 12.10. Launching the ASP.NET SQL Server Setup Wizard

image

Note

Your Windows account must have sufficient permissions to create the new objects in the database. The ASP.NET SQL Server Setup Wizard fails if you launch the tool while logged in to Windows without the necessary database permissions.

You use the ASP.NET SQL Server Setup Wizard to connect to a database (anywhere on your network) and create the necessary database objects. For example, the wizard creates the aspnet_Membership, aspnet_Users, aspnet_Roles, and aspnet_UsersInRoles table to store user and role information.

Note

If you don’t want to use the ASP.NET SQL Server Setup Wizard, you can find SQL scripts for adding the application services database objects in the following folder: C:WindowsMicrosoft.NETFrameworkv2.0.50727.

After you create the necessary database objects, you need to modify the database connection used by your MVC application. Modify the ApplicationServices connection string in your web configuration (web.config) file so that it points to the production database. For example, the modified connection in Listing 12.5 points to a database named MyTestDB (the original ApplicationServices connection string has been commented out).

Listing 12.5. Web.config

image


Finally, if you use Integrated Security to connect to your database, you need to add the correct Windows user account as a login to your database. The correct account depends on whether you use the ASP.NET Development Server or Internet Information Services as your web server. The correct user account also depends on your operating system.

If you use the ASP.NET Development Server (the default web server used by Visual Studio), your application executes within the context of your Windows user account. In that case, you need to add your Windows user account as a database server login.

Alternatively, if you use Internet Information Services, you need to add either the ASPNET account or the NT AUTHORITY/NETWORK SERVICE account as a database server login. If you use Windows XP, add the ASPNET account as a login to your database. If you use a more recent operating system, such as Windows Vista or Windows Server 2008, add the NT AUTHORITY/NETWORK SERVICE account as the database login.

You can add a new user account to your database by using Microsoft SQL Server Management Studio (see Figure 12.11).

Figure 12.11. Creating a new Microsoft SQL Server login

image

After you create the required login, you need to map the login to a database user with the right database roles. Double-click the login and select the User Mapping tab. Select one or more application services database roles. For example, to authenticate users, you need to enable the aspnet_Membership_BasicAccess database role. To create new users, you need to enable the aspnet_Membership_FullAccess database role (see Figure 12.12).

Figure 12.12. Adding Application Services database roles

image

After you complete all these steps, your user account information will be stored in your production database. If you want to verify that everything is set up correctly, you can create a new user account within your ASP.NET MVC application and check whether the new user is added to the aspnet_Users table (see Figure 12.13).

Figure 12.13. Adding a user to the production database

image

Configuring Membership Settings

By default, ASP.NET MVC uses the SQL membership provider. This provider exposes several configuration settings that you can use to customize the behavior of the provider:

ApplicationName—The name of the ASP.NET application associated with the membership information.

Description—The description of the provider that is displayed in administrative tools.

EnablePasswordReset—When true, users can reset their password to a random password.

EnablePasswordRetrieval—When true, users can recover their password.

MaxInvalidPasswordAttempts—An integer that represents the number of log on attempts after which the user account is locked.

MinRequiredNonalphanumericCharacters—The minimum number of characters other than an alphabetic or numeric character that must appear in a password (for example, #%).

MinRequiredPasswordLength—An integer that represents the minimum length of a valid password.

Name—The friendly name for this provider.

PasswordAttemptWindow—An integer that represents an interval in minutes. If you enter the wrong password more than the MaxInvalidPasswordAttempts within this interval, your account is locked.

PasswordFormat—Determines how passwords are stored in the database. The three possible values are Clear, Encypted, and Hashed.

PasswordStrengthRegularExpression—A regular expression that a valid password must match.

RequiresQuestionAndAnswer—When true, you must answer a question before you can retrieve or reset your password.

RequiresUniqeEmail—When true, you cannot create two accounts with the same email address.

The membership provider is configured with the settings in Listing 12.6 in the root web configuration (web.config) file.

Listing 12.6. Web.config (membership)

image


You can toughen the requirements for a valid password by modifying these default settings. For example, if you want to require a password to be at least 10 characters and contain at least 1 nonalphanumeric characters, you can change the minRequiredPasswordLength and minRequiredNonalphanumericCharacters settings.

Notice the passwordFormat setting. By default, passwords are hashed with a one-way hash before stored in the database. When a user logs on, the password is hashed and compared against the hashed value in the database. So the actual user passwords are not stored anywhere.

The advantage of hashing passwords is that if your database server is ever compromised by an evil hacker, the hacker can’t steal any of your website user passwords. The disadvantage of hashing passwords is that you can never recover a forgotten password. If a user forgets a password, the password must be reset.

You also can set the passwordFormat setting to the value Clear or Encrypted. When a password is stored with passwordFormat Clear, the password is stored as plain text. When stored with ENCRYPTED, the password is encrypted before being stored in the database.

Using the Membership and Role Manager API

Under the covers, the Account controller uses the Membership API to authenticate and create users. You can work directly with the Membership API in your code.

The Membership API consists of two main classes: the Membership class and the MembershipUser class.

The Membership class exposes a number of useful methods including the following:

CreateUser()

DeleteUser()

FindUsersByEmail()

FindUsersByName()

GeneratePassword()

GetAllUsers()

GetNumberOfUsersOnline()

GetUser()

GetUserNameByEmail()

UpdateUser()

ValidateUser()

Several of these methods return either an individual MembershipUser or a collection of MembershipUser objects. The MembershipUser class has the following methods:

ChangePassword()

ChangePasswordQuestionAndAnswer()

GetPassword()

ResetPassword()

UnlockUser()

The MembershipUser class also has several useful properties:

Comment

CreationDate

Email

IsApproved

IsLockedOut

IsOnline

LastActivityDate

LastLockoutDate

LastLoginDate

LastPasswordChangedDate

PasswordQuestion

UserName

The ASP.NET framework includes a Roles class that you can use to programmatically work with user roles. This Roles class has the following methods:

AddUsersToRole()

AddUsersToRoles()

AddUserToRole()

AddUserToRoles()

CreateRole()

DeleteCookie()

FindUsersInRole()

GetAllRoles()

GetRolesForUser()

GetUsersInRole()

IsUserInRole()

RemoveUserFromRole()

RemoveUserFromRoles()

RemoveUsersFromRole()

RemoveusersFromRoles()

RoleExists()

Let’s look at a concrete sample of how you can use one of these classes. We create a lookup form in which you can search for a user by name. The results display whether each user is currently online.

The Lookup controller is contained in Listing 12.7.

Listing 12.7. ControllersLookupController.cs (C#)

image


Listing 12.7. ControllersLookupController.vb (VB)

image


The controller has a single action named Index(). The Index() action retrieves all the users that match the search parameter passed to the action. Notice that the FindUsersByName() accepts the % wildcard.

The Index view is contained in Listing 12.8. Notice that it uses the IsOnline property to display whether each user is currently online (see Figure 12.14).

Figure 12.14. Lookup form for users

image

Listing 12.8. ViewsLookupIndex.aspx (C#)

image


Listing 12.8. ViewsLookupIndex.aspx (VB)

image


Using Windows Authentication

By default, an ASP.NET MVC application is configured to use Forms authentication and the SQL membership provider. If you want to authenticate your users against their Windows credentials, you can use Windows authentication.

In the following sections, you learn how to configure Windows authentication and use Windows authentication to authorize Windows users and groups.

Note

Another option, which we don’t discuss in this book, is to use the Active Directory membership provider. You can use the Active Directory membership provider with Forms authentication to authenticate users against Active Directory.

Configuring Windows Authentication

You can switch from Forms authentication to Windows authentication by changing the authentication section in your application root web configuration (web.config) file. You need to modify the authentication section so that it looks like Listing 12.9.

Listing 12.9. Web.config (Enabling Windows Authentication)

image


After you switch to Windows authentication, you delegate responsibility for authenticating users to your web server. In other words, the ASP.NET Development Server or Internet Information Services is responsible for authenticating users.

First, while developing an MVC application, you use the ASP.NET Development Web Server included with Visual Studio. By default, the ASP.NET Development Web Server executes all pages in the context of the current Windows account (whatever account you used to log in to Windows).

The ASP.NET Development Web Server also supports NTLM authentication. You can enable NTLM authentication by right-clicking the name of your project in the Solution Explorer window and selecting Properties. Next, select the Web tab and check the NTLM check box (see Figure 12.15).

Figure 12.15. Enabling NTLM authentication for the ASP.NET Development Web Server

image

For a production web application, on the other hand, you use IIS as your web server. IIS supports several types of authentication including the following:

Basic authentication—Defined as part of the HTTP 1.0 protocol. Sends user names and passwords in clear text (Base64 encoded) across the Internet.

Digest authentication—Sends a hash of a password, instead of the password itself, across the internet.

Integrated Windows (NTLM) authentication—The best type of authentication to use in intranet environments using windows.

Certificate authentication—Enables authentication using a client-side certificate. The certificate maps to a Windows user account.

You can use Internet Information Services Manager to enable a particular type of authentication. Be aware that all types of authentication are not available in the case of every operating system. Furthermore, if you use IIS 7.0 with Windows Vista, you need to enable the different types of Windows authentication before they appear in the Internet Information Services Manager. Open Control Panel, Programs, Programs and Features; Turn Windows features on or off; and expand the Internet Information Services node (see Figure 12.16).

Figure 12.16. Enabling Windows IIS features

image

Using Internet Information Services, you can enable or disable different types of authentication. For example, Figure 12.17 illustrates disabling anonymous authentication and enabling Integrated Windows (NTLM) authentication when using IIS 7.0.

Figure 12.17. Enabling integrated Windows authentication

image

Authenticating Windows Users and Groups

After you enable Windows authentication, you can authorize Windows users and groups in the same way as you authorize users and roles when using Forms authentication.

When Windows authentication is enabled, the Authorize attribute works against Windows users and groups. For example, the controller in Listing 12.10 prevents anyone from accessing the Index() action except for the user with the account redmondswalther.

Listing 12.10. ControllersWindowsController.cs (C#)

image


Listing 12.10. ControllersWindowsController.vb (VB)

image


Exactly what happens when a user attempts to access a controller action and the user is not authorized depends on the type of authentication enabled in the web server. For example, when using the ASP.NET Development Server, you simply get a blank page. The page is served with a 401 Not Authorized HTTP Response Status.

If, on the other hand, you use IIS with Anonymous authentication disabled and Basic authentication enabled, you keep getting a login dialog prompt each time you request the protected page (see Figure 12.18).

Figure 12.18. Basic authentication login dialog

image

You also can use the Authorize attribute with Windows groups. That way, you can prevent an action from being invoked by anyone who is not the member of the right group.

Before you can use Windows groups, you need to enable the Windows token role provider. You can enable this provider by modify the roleManager section in your root web configuration (web.config) file to look like Listing 12.11.

Listing 12.11. Web.Config (roleManager)

image


In Listing 12.11, the roleManager element has been modified with a defaultProvider attribute that points at the Windows token role provider.

After you make this change, you can use the Authorize attribute with Windows groups. For example, the SalesFigures() action in Listing 12.12 can be invoked only by members of the Windows Managers group.

Listing 12.12. ControllersWindowsController.cs (SalesFigures) (C#)

image


Listing 12.12. ControllersWindowsController.vb (VB)

image


You also can programmatically verify a user’s group. This approach is taken in the SecretStuff() action in Listing 12.13.

Listing 12.13. ControllersWindowsController.cs (SecretStuff) (C#)

image


Listing 12.13. ControllersWindowsController.vb (SecretStuff) (VB)

image


Note

Because of Windows User Account Control (UAC), when working with Windows Vista or Windows Server 2008, the local Administrators group behaves differently than other groups. The [Authorize] attribute won’t correctly recognize a member of the local Administrators group unless you modify your computer’s UAC settings.

Testing Authorization

In this final section, we discuss two methods for testing authentication and authorization. In particular, you learn how to test that only the right user or right role has access to a controller action.

Testing for the Authorize Attribute

If you are using the Authorize attribute to protect controller actions, the easiest way to test authorization is to simply check for this attribute. For example, the controller in Listing 12.14 uses the Authorize attribute. The Authorize attribute prevents anyone except for Jack from invoking the Index() action.

Listing 12.14. ControllersJackController.cs (C#)

image


Listing 12.14. ControllersJackController.vb (VB)

image


If you want to verify that the Authorize attribute is present on the Index() action, you can use the test in Listing 12.15. This test verifies that there is, in fact, an Authorize attribute on the Index() action and the Authorize attribute has a Users property set to the value "Jack".

Listing 12.15. ControllersJackControllerTests.cs (C#)

image


Listing 12.15. ControllersJackControllerTests.vb (VB)

image


Personally, I find something unsatisfying about this test. The test does not actually capture our requirement. We want to test that only Jack is authorized to access the Index action. However, the test simply verifies that there is an Authorize attribute on the action.

Testing with the User Model Binder

In this section, we explore an alternative method of testing authorization. Instead of testing for the Authorize attribute, we use a custom model binder.

In Chapter 7, “Understanding Model Binders and Action Filters,” we created a custom model binder named the user model binder. The user model binder enables you to pass a user (an IPrincipal object) as a parameter to a controller action.

You register the user model binder in an application by adding the line of code to the Global.asax file contained in Listing 12.16.

Listing 12.16. Global.asax (C#)

image


Listing 12.16. Global.asax (VB)

image


After you register the user model binder, you can add an additional IPrincipal parameter to any controller action. For example, the controller action in Listing 12.17 displays the Index view only when the current user is Jill.

Listing 12.17. ControllersJillController.cs (C#)

image


Listing 12.17. ControllersJillController.vb (VB)

image


The controller action in Listing 12.17 is easy to test. The test class in Listing 12.18 contains two tests. The first test verifies that Jill can invoke the Index() action, and the second test verifies that Jack cannot invoke the controller action. Jill gets a view and Jack gets an HTTP Unauthorized result.

Listing 12.18. ControllersJillControllerTests.cs (C#)

image

image


Listing 12.18. ControllersJillControllerTests.vb (VB)

image


When you run the tests in Listing 12.18, both tests pass. This should give you some confidence that only Jill can invoke the Index action (see Figure 12.19).

Figure 12.19. Passing authorization tests

image

The tests in Listing 12.18 make use of a FakePrincipal class. This (extremely simple) class is contained in Listing 12.19.

Listing 12.19. ModelsFakePrincipal.cs (C#)

image


Listing 12.19. ModelsFakePrincipal.vb (VB)

image


The FakePrincipal class uses a FakeIdentity class. For the sake of completeness, this class is included in Listing 12.20.

Listing 12.20. ModelsFakeIdentity.cs (C#)

image


Listing 12.20. ModelsFakeIdentity.vb (VB)

image


Summary

In this chapter, you learned how to control access to controller actions. In the first section, you learned how to use the Web Site Administration Tool and the Account controller to create users and roles.

Next, you learned how to authorize users both declaratively and programmatically. You learned how to decorate controller actions with the Authorize attribute. You also learned how to take advantage of the User property.

Next, you learned how to configure membership. You learned how to configure a database and your application to store user accounts in a production database server.

We also explored the topic of Windows authentication. You learned how to enable Windows authentication so that you can authorize Windows users and groups.

Finally, we tackled the topic of testing. We examined two ways of testing authorization.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.166.149