Limiting Data Modification Functionality Based on the User

This is the Visual Basic tutorial    (Switch to the Visual C# tutorial)

In this tutorial, we’ll see how to limit the data modification functionality of an editable DataList, based on the currently logged on user.

Created by Scott Mitchell

Download the code for this tutorial   |   Download the tutorial in PDF format

Introduction

A number of web applications support user accounts and provide different options, reports, and functionality based on the logged on user. Back in the Limiting Data Modification Functionality Based on the User tutorial, we examined how to dynamically adjust the data modification capabilities of a DetailsView and GridView based on the visiting user. Specifically, this earlier tutorial allowed users to “log on” to the website either as a user from a supplier or as an employee of our company (Northwind Traders). If the “logged on” user was a supplier, they were allowed to update information about their own products and company. Users from Northwind Traders were able to update product and supplier information from any company.

Note: Recall that in the Limiting Data Modification Functionality Based on the User tutorial a user “logged on” to the website by choosing their level of access from a drop-down list (whether they could edit all suppliers or just a particular one). ASP.NET 2.0’s membership system provides a standardized, extensible platform for creating, managing, and validating user accounts. However, an examination of the membership system is beyond the scope of these tutorials. For more on membership, refer to my Examining ASP.NET 2.0’s Membership, Roles, and Profile article series.

In this tutorial, we’ll see how to limit the data modification functionality based on the currently logged on user, but do so using the DataList control. In particular, we’ll create a page that lists employee information — their name, title, and hire date — in an editable DataList. Anonymous users cannot edit any employees (see Figure 1), but a “logged on” user can edit her own employee record plus any the records of any employee she manages (see Figure 2). Any employee that does not have a manager can edit any employee’s record.

Anonymous Users Cannot Edit Any Employee’s Record

Figure 1: Anonymous Users Cannot Edit Any Employee’s Record (Click to view full-size image)

A “Logged On” User Can Edit Her Own Record and the Records of Those Employees She Manages

Figure 2: A “Logged On” User Can Edit Her Own Record and the Records of Those Employees She Manages (Click to view full-size image)

Since the employee’s managerial position dictates whose records they can edit, when testing this tutorial’s example it’s important to be familiar with Northwind Trader’s organizational hierarchy. Use the hierarchy shown in Figure 3 as a reference when testing.

The Northwind Traders Organizational Hierarchy

Figure 3: The Northwind Traders Organizational Hierarchy (Click to view full-size image)

Let’s get started!

Step 1: “Logging On” to the Website

In a real-world ASP.NET 2.0 application, we’d take advantage of the membership system and the security Web controls to persist user account information, authenticate users, associate user accounts with employees, and so on. To keep the focus on working with data, we’ll forgo setting up membership and instead simulate authentication by allowing the visitor to pick which employee they want to be logged on as from a DropDownList.

Start by opening the UserLevelAccess.aspx page in the EditDeleteDataList folder and add a DropDownList to the page, setting its ID property to LoggedOnAs. From the DropDownList’s smart tag, create a new ObjectDataSource named LoggedOnAsDataSource and configure it to use the EmployeesBLL class’s GetEmployees() method (see Figure 4). Complete the DropDownList’s data source configuration by having the LastName data field displayed and EmployeeID as the value for each list item, as shown in Figure 5.

Configure the ObjectDataSource Control to Use the EmployeesBLL Class’s GetEmployees() Method

Figure 4: Configure the ObjectDataSource Control to Use the EmployeesBLL Class’s GetEmployees() Method (Click to view full-size image)

Have Each List Item Display LastName and Use EmployeeID as the Value

Figure 5: Have Each List Item Display LastName and Use EmployeeID as the Value (Click to view full-size image)

At this point, the LoggedOnAs DropDownList will display the last name of each employee. In addition to being able to render the appropriate data modification user interface for “logged on” users, we also need to be able to handle anonymous users. Therefore, let’s add a static item to this DropDownList labeled “Anonymous Visitor” with a value of “-1” (see Figure 6).

Add an “Anonymous Visitor” List Item to the LoggedOnAs DropDownList

Figure 6: Add an “Anonymous Visitor” List Item to the LoggedOnAs DropDownList

Be sure to set the DropDownList’s AppendDataBoundItems property to True. By default, data bound items — such as the employees bound from the ObjectDataSource — will overwrite any statically-added items. By setting this property to True, the data bound employee list items will be appended to the static one (“Anonymous Visitor”). Finally, set the DropDownList’s AutoPostBack property to True.

After adding the DropDownList and ObjectDataSource and configuring the DropDownList’s properties, your page’s declarative markup should look similar to the following:

You are logged on as: <asp:DropDownList ID="LoggedOnAs" AutoPostBack="True" AppendDataBoundItems="True" DataSourceID="LoggedOnAsDataSource" DataTextField="LastName" DataValueField="EmployeeID" runat="server"> <asp:ListItem Value="-1">Anonymous Visitor</asp:ListItem> </asp:DropDownList> <asp:ObjectDataSource ID="LoggedOnAsDataSource" runat="server" OldValuesParameterFormatString="original_{0}" SelectMethod="GetEmployees" TypeName="EmployeesBLL"> </asp:ObjectDataSource>

Take a moment to view our progress through a browser. The drop-down list contains an “Anonymous Visitor” option along with the last name of each of the employees in the organization.

The Drop-Down List Contains an “Anonymous Visitor” Option and a List Item for Each Employee

Figure 7: The Drop-Down List Contains an “Anonymous Visitor” Option and a List Item for Each Employee (Click to view full-size image)

A visitor can “log on” to the site by selecting an employee from the drop-down list, and they will be considered signed in as that user.

Step 2: Creating the Editable DataList

With the faux login interface complete, our next step is to build the editable DataList and limit its data modification capabilities based on the “logged on” user. Before concerning ourselves with limiting the functionality, let’s first create a DataList in which every employee is editable regardless of who is “logged on.” Once we’ve completed the editable DataList, we will finish by limiting the editing capabilities based on the user visiting the page.

As we’ve seen in previous tutorials, creating an editable DataList involves:

  • Adding the DataList and creating its read-only interface
  • Creating the editing interface
  • Writing event handlers for the DataList’s EditCommand, CancelCommand, and UpdateCommand events

Let’s tackle each of these steps separately.

Building the Read-Only Interface

Start by dragging a DataList control from the Toolbox onto the Designer. Set its ID property to Employees and, from its smart tag, create a new ObjectDataSource named EmployeesDataSource. Configure the ObjectDataSource to return all of the employee information using the EmployeesBLL class’s GetEmployees() method, just like we did with the LoggedOnAsDataSource (refer back to Figure 4, if needed).

Once the data source has been configured, Visual Studio will create the default ItemTemplate for the DataList, displaying each data field’s name and value. Edit the ItemTemplate so that it shows just the employee name, title, and hire date. Also add a Button, LinkButton, or ImageButton control for the Edit button. Recall that this button’s CommandName property must be set to “Edit”. After these additions, the ItemTemplate should look as follows:

<ItemTemplate> <h4> <asp:Label ID="FirstNameLabel" runat="server" Text='<%# Eval("FirstName") %>' /> <asp:Label ID="LastNameLabel" runat="server" Text='<%# Eval("LastName") %>' /> </h4> Title: <asp:Label ID="TitleLabel" runat="server" Text='<%# Eval("Title") %>' /> <br /> Hire Date: <asp:Label ID="HireDateLabel" runat="server" Text='<%# Eval("HireDate", "{0:d}") %>' /> <br /> <br /><br /> <br /><br /> <asp:Button runat="server" ID="EditButton" CommandName="Edit" Text="Edit" /> <br /><br /> </ItemTemplate>

Note: The databinding expression assigned to the HireDateLabel’s Text property specifies a short date time format. The format specifier is passed in as the second parameter to the Eval method. For more information on specifying formatting information via databinding syntax, refer back to the Using TemplateFields in the DetailsView Control tutorial.

Figure 8 shows the page when viewed through a browser. Since an EditCommand event handler hasn’t been created yet, clicking the Edit button causes a postback, but does not make the item editable.

Each Employee’s Name, Title, and Hire Date are Displayed

Figure 8: Each Employee’s Name, Title, and Hire Date are Displayed (Click to view full-size image)

Creating the Editing Interface

With the read-only interface complete, our next task is to build the editing interface. For our editing interface — spelled out in the DataList’s EditItemTemplate — use a TextBox for each of the four data fields. Next, add the Update and Cancel buttons. Don’t forget to set the button CommandName properties to “Update” and “Cancel”, respectively. Additionally, add RequiredFieldValidators to ensure that the first and last names are provided and use a CompareValidator to check that the hire date value is a valid date. Also include a ValidationSummary control on the page and set the Cancel button’s CausesValidation property to False.

Note: For more information on working with the validation controls in ASP.NET 2.0, peruse the Validating Form Input Controls section of the ASP.NET QuickStart Tutorials.

Since we’ve built editing interfaces in previous tutorials, let’s skip straight ahead to what the resulting declarative markup should look like. For a walkthrough of creating the DataList’s editing interface, consult the Overview of Editing and Deleting Data in the DataList and Customizing the DataList’s Editing Interface tutorials.

<EditItemTemplate> First Name: <asp:TextBox runat="server" ID="FirstName" Columns="10" MaxLength="10" Text='<%# Eval("FirstName") %>' /> <asp:RequiredFieldValidator ID="RequiredFieldValidator1" ControlToValidate="FirstName" ErrorMessage="You must provide the employee's first name." runat="server">*</asp:RequiredFieldValidator> <br /> Last Name: <asp:TextBox runat="server" ID="LastName" Columns="20" MaxLength="20" Text='<%# Eval("LastName") %>' /> <asp:RequiredFieldValidator ID="RequiredFieldValidator2" ControlToValidate="LastName" ErrorMessage="You must provide the employee's last name." runat="server">*</asp:RequiredFieldValidator> <br /> Title: <asp:TextBox runat="server" ID="Title" Text='<%# Eval("Title") %>' Columns="30" MaxLength="30" /> <br /> Hire Date: <asp:TextBox runat="server" ID="HireDate" Columns="15" Text='<%# Eval("HireDate", "{0:d}") %>' /> <asp:CompareValidator ID="CompareValidator1" ControlToValidate="HireDate" ErrorMessage="The hire date must be a valid date value." Operator="DataTypeCheck" Type="Date" runat="server">*</asp:CompareValidator> <br /> <br /> <asp:Button ID="UpdateButton" runat="server" Text="Update" CommandName="Update" />     <asp:Button ID="CancelButton" Text="Cancel" CausesValidation="False" CommandName="Cancel" runat="server" /> </EditItemTemplate>

Currently, the editing interface cannot be seen when visiting the web page because the EditCommand event handler that will mark a particular DataList item for editing has yet to be created. We can, however, view the editing interface through the Designer. From the DataList’s smart tag select the “Edit Templates” option and choose EditItemTemplate.

Preview the Editing Interface Through the Designer

Figure 9: Preview the Editing Interface Through the Designer (Click to view full-size image)

Writing the EditCommand, CancelCommand, and UpdateCommand Event Handlers

With the ItemTemplate and EditItemTemplate finished, the final step is to write the event handlers for the DataList’s EditCommand, CancelCommand, and UpdateCommand events. These are the events that fire when the user clicks the Edit, Cancel, and Update buttons. The EditCommand and CancelCommand events simply update the EditItemIndex property and rebind the data to the DataList — the EditCommand event handler assigns the EditItemIndex to the index of the item whose Edit button was clicked whereas the CancelCommand event handler reverts it back to a value of -1:

Protected Sub Employees_EditCommand(source As Object, e As DataListCommandEventArgs) _ Handles Employees.EditCommand 'Set the EditItemIndex to the index of the item whose Edit button was clicked Employees.EditItemIndex = e.Item.ItemIndex Employees.DataBind() End Sub Protected Sub Employees_CancelCommand(source As Object, e As DataListCommandEventArgs) _ Handles Employees.CancelCommand 'Return the DataList to its pre-editing state Employees.EditItemIndex = -1 Employees.DataBind() End Sub

After adding these two event handlers, take a moment to view the page in a browser. As shown in Figure 10, clicking the Edit button marks an employee as editable and renders the item using the EditItemTemplate. Clicking the Cancel button returns the DataList to its pre-editing state without saving any changes.

The Edit and Cancel Buttons are Now Functional

Figure 10: The Edit and Cancel Buttons are Now Functional (Click to view full-size image)

The UpdateCommand event handler is responsible for propagating the user’s changes to the database. This is accomplished by:

  • Consulting the DataList’s DataKeys collection to grab the edited employee’s EmployeeID value.
  • Reading in the values from the editing interface. As we saw in past tutorials, this can be accomplished by using the FindControl("controlID") method to programmatically reference the appropriate TextBox in the EditItemTemplate.
  • Calling the proper BLL method to update the employee record.

Currently, our application architecture does not include any means to update employee information. To add such functionality, we’d need to add a method to the EmployeesDataTable in the DAL as well as a corresponding method to the EmployeesBLL class in the BLL. The Creating a Data Access Layer and Creating a Business Logic Layer tutorials explore these steps in detail.

The thrust of this tutorial is to illustrate customizing the data modification access rights based on the currently logged on user. Therefore, I am going to leave this as an exercise for the reader and will instead create a simple UpdateCommand event handler that displays a client-side messagebox explaining that the updating capabilities are not yet implemented:

Protected Sub Employees_UpdateCommand(source As Object, e As DataListCommandEventArgs) _ Handles Employees.UpdateCommand ' Display a client-side messagebox explaining that the updating capabilities ' are not yet implemented Page.ClientScript.RegisterStartupScript _ (Me.GetType(), "NotYetImplemented", _ "alert('Update capabilities are not yet implemented and " + _ "are left as an exercise to the reader - that\'s you!');", _ True) ' Return the DataList to its pre-editing state Employees.EditItemIndex = -1 Employees.DataBind() End Sub

Note: The Page.ClientScript property is an instance of the ClientScriptManager class, which contains a number of methods for programmatically injecting client-side script into the rendered output of an ASP.NET page. See Client Script in ASP.NET Web Pages and Working with Client-Side Script for more information.

Clicking the Update button displays the modal dialog box shown in Figure 11.

Adding Update Support is an Exercise Left for the Reader

Figure 11: Adding Update Support is an Exercise Left for the Reader (Click to view full-size image)

Step 3: Limiting Editing Capabilities Based on the “Logged On” User

Currently, any user — including anonymous visitors — is able to edit any employee’s information. We want to prohibit this and instead only allow authenticated users to edit their records and the records of the employees they manage (if any), with the one exception being if an employee has no managers, then he can edit any employee.

To prevent a visitor from editing employees she is not authorization to edit, we can hide those employees’ Edit buttons. The DataList’s ItemDataBound event fires once for each record bound to the DataList. We can create an event handler that determines if the currently “logged on” user has permission to edit the record and show or hide the Edit button accordingly:

' A page-level variable holding information about the currently "logged on" user Dim currentlyLoggedOnUser As Northwind.EmployeesRow = Nothing Protected Sub Employees_ItemDataBound(sender As Object, e As DataListItemEventArgs) _ Handles Employees.ItemDataBound If e.Item.ItemType = ListItemType.AlternatingItem OrElse _ e.Item.ItemType = ListItemType.Item Then ' Determine the Manager of the Employee record being bound to this DataListItem Dim employee As Northwind.EmployeesRow = _ CType(CType(e.Item.DataItem, DataRowView).Row, Northwind.EmployeesRow) ' Read in the information for the currently "logged on" user, if needed If currentlyLoggedOnUser Is Nothing AndAlso _ Convert.ToInt32(LoggedOnAs.SelectedValue) > 0 Then Dim employeeAPI As New EmployeesBLL() currentlyLoggedOnUser = _ employeeAPI.GetEmployeeByEmployeeID( _ Convert.ToInt32(LoggedOnAs.SelectedValue))(0) End If ' See if this user has access to edit the employee Dim canEditEmployee As Boolean = False If currentlyLoggedOnUser IsNot Nothing Then ' We've got an authenticated user... see if they have no manager... If currentlyLoggedOnUser.IsReportsToNull() Then canEditEmployee = True Else ' ok, this person has a manager... see if they are editing themselves If currentlyLoggedOnUser.EmployeeID = employee.EmployeeID Then canEditEmployee = True ' see if this person manages the employee ElseIf Not employee.IsReportsToNull() AndAlso _ employee.ReportsTo = currentlyLoggedOnUser.EmployeeID Then canEditEmployee = True End If End If End If ' Referrence the Edit button and set its Visible property accordingly Dim editButton As Button = CType(e.Item.FindControl("EditButton"), Button) editButton.Visible = canEditEmployee End If End Sub

Before the ItemDataBound event handler is a page-level variable currentlyLoggedOnUser. This variable holds information about the employee the visitor has selected from the LoggedOnAs DropDownList and is populated on demand in the ItemDataBound event handler. In a real-world application, where authentication and authorization are handled formally, such data would likely be accessed when authenticating the user and persisted through session variables, the authentication ticket, or some other means.

Since the ItemDataBound event fires for all types of items added to the DataList — headers, footers, separators, items, editable items, and so on — the event handler begins by ensuring that we’re dealing with an item or alternating item. Next, the employee data that was just bound to this DataListItem is referenced via the DataItem property and cast to an EmployeeRow object. Following that, information about the currently “logged on” user is read into the page-level currentlyLoggedOnUser variable, if needed.

Once we have the information about the currently “logged on” user and the employee whose record was just bound to the DataList, we can apply our logic to determine whether the employee record can be edited. If currentlyLoggedOnUser is still Nothing by this point, then we’re dealing with an anonymous user who cannot edit any employee records. If it’s not Nothing, we check to see if the authenticated user does not report to anyone. If so, then they can edit any employee. If the authenticated user does report to someone, however, they are only able to edit the current employee record if it is theirs or if they are the employee’s manager.

Finally, the Edit button is programmatically referenced via the FindControl("controlID") method and its Visible property is set.

When first visiting the page through a browser, the “Anonymous Visitor” option is selected by default, therefore hiding the Edit buttons in all employee records (see Figure 12).

Anonymous Visitors Cannot Edit Any of the Employee Records

Figure 12: Anonymous Visitors Cannot Edit Any of the Employee Records (Click to view full-size image)

Selecting a different employee from the LoggedOnAs DropDownList causes a postback (since we set its AutoPostBack property to True), but does not update the DataList. Since the DataList’s ObjectDataSource doesn’t use any parameters based on the DropDownList, its changes do not require the data to be rebound to the DataList. Therefore, we need to manually instruct the DataList to rebind its data whenever the DropDownList’s selected index is changed.

Create an event handler for the DropDownList’s SelectedIndexChanged event and add the following code:

Protected Sub LoggedOnAs_SelectedIndexChanged(sender As Object, e As EventArgs) Handles _ LoggedOnAs.SelectedIndexChanged ' Make sure editing is disabled and rebind the data to the DataList Employees.EditItemIndex = -1 Employees.DataBind() End Sub

Invoking the DataList’s DataBind() method instructs the DataList to rebind its data. This causes the ItemDataBound event to fire again as each item is added, causing the Edit buttons to be shown or hidden based on the newly selected user from the LoggedOnAs DropDownList. Before calling the DataBind() method, set the EditItemIndex property to -1 to ensure that the DataList is rendered in its read-only state. If this line of code is omitted and a user clicks the Edit button for a particular DataList item and then selects a new user from the LoggedOnAs DropDownList, the edited item will remain in edit mode. We want to prohibit this since the user may be changing who they are “logged on” as from someone that can edit that employee record to one who cannot.

After completing the SelectedIndexChanged event handler, take a minute to test the page in a browser. As Figure 13 shows, when logged on as Davolio, only Nancy Davolio’s information is editable, but when logged on as Buchanan there are four editable records — Steven Buchanan and his three subordinates: Michael Suyama, Robert King, and Anne Dodsworth (see Figure 14). Finally, when “logged on” as Fuller, all employee records are editable, as shown in Figure 15.

Davolio Can Only Edit Her Own Employee Information

Figure 13: Davolio Can Only Edit Her Own Employee Information (Click to view full-size image)

Buchanan Can Edit Four Employee Records — His Own and His Subordinates’

Figure 14: Buchanan Can Edit Four Employee Records — His Own and His Subordinates’ (Click to view full-size image)

Fuller has No Manager, and Therefore Can Edit All Employee Records

Figure 15: Fuller has No Manager, and Therefore Can Edit All Employee Records (Click to view full-size image)

Note: If the rules for editing employee information — that employees can only edit their own records and those of their subordinates, unless they report to no one, in which case they can edit all records — are universal across the application and not specific to just this web page, it would be prudent to also add this check in the BLL. In the EmployeesBLL class’s UpdateEmployee method (which was left as an exercise to the reader), you could raise an exception if the currently logged on user didn’t have authorization to edit the specific employee’s records. This exception could then be gracefully handled in the ASP.NET web page. Refer back to the Handling BLL- and DAL-Level Exceptions tutorial for more information on throwing and handling exceptions among the layers of the architecture.

Summary

Most sites that provide user accounts need to customize the data modification interface based upon the currently logged on user. Administrative users may be able to delete and edit any record, whereas non-administrative users may be limited to only updating or deleting records they created themselves. Whatever the scenario may be, the DataList and Business Logic Layer classes can be extended to add or deny certain functionality based on the logged on user. In this tutorial we saw how to limit what records were editable based upon the “logged on” user.

This tutorial concludes our examination of inserting, updating, and deleting data using the DataList. Starting with the next tutorial, we’ll turn our attention to adding paging and sorting support.

Happy Programming!

About the Author

Scott Mitchell, author of seven ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Scott works as an independent consultant, trainer, and writer. His latest book is Sams Teach Yourself ASP.NET 2.0 in 24 Hours. He can be reached at mitchell@4GuysFromRolla.com. or via his blog, which can be found at http://ScottOnWriting.NET.

Special Thanks To…

This tutorial series was reviewed by many helpful reviewers. Lead reviewer for this tutorial was Dennis Patterson. Interested in reviewing my upcoming MSDN articles? If so, drop me a line at mitchell@4GuysFromRolla.com.

Next Tutorial

Visual Basic Tutorials

(Switch to Visual C# tutorials)

Scott’s Book

This tutorial was written by Scott Mitchell who has also written the following book on learning ASP.NET.

»  More Books

Page view counter
Microsoft Communities