[ This document was written for WCF Services Version 1 Service Pack 2 and might not be up to date
Please see Release Notes or Changelog for a list of changes since WCF RIA Services ]
The Silverlight Business Application template creates a solution that automatically enables authentication (with Forms Authentication for the authentication mode), roles, and profiles. The solution includes data forms for logging in existing users and registering new users. You can use these features without writing any additional code. You can customize the solution by defining roles and profile properties.
In this walkthrough, you will learn how to use authentication, roles, and profiles in a Silverlight Business Application. You will restrict access to certain domain operations based on the user's credentials and customize the user interface based on user preferences. You will use the ASP.NET Web Site Administration Tool for managing roles and users in the site.
Prerequisites
This and the other walkthroughs presented in the Open Ria Services documentation require several prerequisite programs, such as Visual Studio and the Silverlight Developer Runtime and SDK, be installed and configured properly, in addition to Open Ria Services and the Open Ria Services Toolkit. They also require installing and configuring SQL Server 2008 R2 Express with Advanced Services and installing the AdventureWorks OLTP and LT database.
Detailed instructions for the satisfaction of each of these prerequisites are provided by the topics within the Prerequisites for Open Ria Services node. Follow the instructions provided there before proceeding with this walkthrough to ensure that you encounter as few problems as possible when working through this Open Ria Services walkthroughs.
Creating users and roles
You can use the provided features in a Silverlight Business Application to quickly implement authentication. In the following section, you will use the ASP.NET Configuration tool to create a user and role, and then log in as that user. You will register a new user through the registration form that is provided in the Silverlight Business Application.
To create site, roles and users
In Visual Studio, select File, New, and then Project.
The New Project dialog box appears.
Select the Silverlight project type.
Select the Silverlight Business Application template and name the application ExampleBusinessApplication.
Click OK.
Notice the project structure that is created. The Silverlight client project includes Silverlight pages in the Views folder. These pages enable logging in users and registering new users.
To open the ASP.NET Web Site Administration Tool, first select the server project (ExampleBusinessApplication.Web) in Solution Explorer. open the ASP.NET Configuration tool.
On the Project menu, select ASP.NET Configuration.
If you do not see the ASP.NET Configuration option in the Project menu, it may be because you have selected the client project.
Select the Security tab in the ASP.NET Web Site Administration Tool.
In the Roles section, click the Create or Mange roles link.
Add a new role named Managers and select the Add Role button.
In the lower-right corner, click the Back button.
In the Users section, click the Create user link.
Create a new user with the following values and select the Managers role check box.
User Name: CustomerManager
Password: P@ssword
E-mail: someone@example.com
Security Question: Favorite color?
Security Answer: Blue
Managers role: selected
Click the Create User button.
Close the ASP.NET Web Site Administration Tool.
Run the solution.
The Home page for the application appears in a Web browser.
In the upper-right corner of the page, click the login link.
A Login dialog box appears.
Enter CustomerManager for the user name and P@ssword for the password, and click OK button.
You are now logged in as that user. Notice the text "Welcome CustomerManager" in the upper-right corner.
Click the logout link.
You are no longer logged in as CustomerManager. In the following steps, you will create a new user through the registration form.
Click login link again.
In the Login dialog box, click the Register now link.
The registration form is now displayed.
Fill out the registration form to create a new user account. Use the following values for the new user.
Username: SalesUser
Friendly name: SalesUser
Email: someone@example.com
Password: P@ssword
Security Question: What was the color of your first car?
Security Answer: Green
Click OK to create the new user.
Notice that you are now logged in as SalesUser.
Close the browser.
Open the ASP.NET Web Site Administration Tool again, and click the Security tab.
Notice that 2 users now exist in the site and 2 roles exist even though you have only created one role.
Click Create or Manage roles and notice the Managers role and the Registered Users.
The Registered Users role was automatically created by the Business Application template.
For Registered Users, click the Manage link.
Notice that the user named SalesUser that you added through the application is in the Registered Users role.
Close the ASP.NET Web Site Administration Tool.
Defining Access and profile properties
You restrict access to a domain operation by applying either the RequiresAuthenticationAttribute attribute or the RequiresRoleAttribute attribute to the domain operation. Domain operations without an attribute are available to all users. Applying an attribute to domain operation does not prevent the user from calling the domain operation; however, users without the required credentials will receive an exception.
Restrict Displayed Data by Roles
In Solution Explorer, right-click the App_Data folder in the server project, select Add and then Existing Item.
In the Add Existing Item dialog box, add the AdventureWorksLT sample database.
In the server project, add a new item and select the ADO.NET Entity Data Model template from the Data templates.
Name the model AdventureWorksModel.edmx and click Add.
The Entity Data Model Wizard appears.
Select the Generate from database option and then click Next.
Select the AdventureWorksLT database and then click Next.
From the list of database objects, select the Customer, Product, and SalesOrderHeader tables, and then click Finish.
The entity data model appears in the designer.
Build the solution.
In the server project, add a new item and select the Domain Service Class template from the Web templates.
Name the domain service AdventureWorksDomainService and then click Add.
In the Add New Domain Service Class dialog box, select the Customer, Product, and SalesOrderHeader entities.
Click OK to finish creating the domain service.
In the AdventureWorksDomainService class file, add the RequiresAuthenticationAttribute attribute to GetSalesOrderHeader method.
<RequiresAuthentication()> _
Public Function GetSalesOrderHeaders() As IQueryable(Of SalesOrderHeader)
Return Me.ObjectContext.SalesOrderHeaders
End Function
The GetProducts domain operation is available to any user, GetSalesOrderHeaders is available to authenticated users, and GetCustomers is available to only users in the Managers role.
The following shows the complete domain service.
<EnableClientAccess()> _
Public Class AdventureWorksDomainService
Inherits LinqToEntitiesDomainService(Of AdventureWorksLT_DataEntities)
<RequiresRole("Managers")> _
Public Function GetCustomers() As IQueryable(Of Customer)
Return Me.ObjectContext.Customers
End Function
Public Function GetProducts() As IQueryable(Of Product)
Return Me.ObjectContext.Products
End Function
<RequiresAuthentication()> _
Public Function GetSalesOrderHeaders() As IQueryable(Of SalesOrderHeader)
Return Me.ObjectContext.SalesOrderHeaders
End Function
End Class
You define a profile property in the Web.config file. When you add the property to the User class on the server, the corresponding property is generated for the client project.
Add profile properties
In the server project, open the Web.config file.
In the \ element, add a profile property named DefaultRows. The property will contain the user's preference for the number of rows to display.
The following shows the profile section of the Web.config file.
Open the User.cs or User.vb file, and add a property named DefaultRows.
Imports OpenRiaServices.Server.Authentication
Imports System.Runtime.Serialization
Namespace Web
Partial Public Class User
Inherits UserBase
Public Property FriendlyName As String
Public Property DefaultRows As Integer
End Class
End Namespace
Before calling a domain operation with restricted permissions, you should check that the user has the required credentials; otherwise, an exception is thrown. In the following section, you will check the user's credentials and populate from one to three DataGrid controls based on the user's credentials. You will also retrieve the number of records based on a property in the user profile. A default value of 10 is used for users that are not authenticated. This section does not include a way for users to set the DefaultRows profile property, but you will add that in a later section.
Add a Silverlight Page for displaying data
In the client project, add a new item to the Views folder.
Select the Silverlight Page template and name the new page Reports.xaml.
Open the MainPage.xaml file and add a link for the Reports page by adding the following XAML after the HyperlinkButton named Link2 that links to the About page.
In the Assets\Resources folder, open the ApplicationStrings.resx file.
Add a new string resource named ReportsPageTitle with the value of Reports.
Save and close the ApplicationStrings.resx file.
Open the Reports.xaml file, and add the following XAML to the Grid element to match the formatting of the other pages in the site.
<ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">
<StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
<TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
Text="{Binding Path=ApplicationStrings.ReportsPageTitle, Source={StaticResource ResourceWrapper}}"/>
<TextBlock x:Name="ContentText" Style="{StaticResource ContentTextStyle}"
Text="Display reports based on user permissions"/>
</StackPanel>
</ScrollViewer>
Drag three DataGrid controls from the Toolbox to just before the end tag of the stack panel named ContentStackPanel.
When you drag the DataGrid controls from the Toolbox, a reference to the System.Windows.Controls.Data assembly is added to the project and a prefix for the System.Windows.Controls namespace is added to the page.
Name the DataGrid controls ProductsGrid, SalesOrdersGrid, and CustomersGrid.
For each DataGrid control, set the Margin property to 5.
The following example shows the complete Reports.xaml file.
For C#, add using statements for the OpenRiaServices.Client, OpenRiaServices.Client.Authentication, and ExampleBusinessApplication.Web namespaces. For Visual Basic, add Imports statements for the OpenRiaServices.Client, OpenRiaServices.Client.Authentication, System.Windows.Controls, and ExampleBusinessApplication.Web namespaces.
Create an instance of the AdventureWorksDomainContext named context, and create a variable named numberOfRows that contains the number of rows to retrieve.
Private context As New AdventureWorksDomainContext
Private numberOfRows As Integer = 10
Add a method named LoadRestrictedReports that calls the GetSalesOrderHeaderQuery method and the GetCustomersQuery method, if the user belongs to the Managers role, and populates the corresponding data grids with the results.
If you call a domain operation when the user does not have the required credentials, the domain operation returns an exception. You can avoid this situation by checking the credentials before calling the domain operation.
Private Sub LoadRestrictedReports()
Dim loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows))
SalesOrdersGrid.ItemsSource = loadSales.Entities
SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible
If (WebContext.Current.User.IsInRole("Managers")) Then
Dim loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows))
CustomersGrid.ItemsSource = loadCustomers.Entities
CustomersGrid.Visibility = System.Windows.Visibility.Visible
Else
CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
End If
End Sub
Add a method named LoadReports that checks whether the user is authenticated and, if so, calls LoadRestrictedReports method. It also retrieves the profile property named DefaultRows and adds an event handler for the PropertyChanged event on the User object. Finally, it calls the GetProductsQuery method for all users.
Private Sub LoadReports()
If (WebContext.Current.User.IsAuthenticated) Then
numberOfRows = WebContext.Current.User.DefaultRows
AddHandler WebContext.Current.User.PropertyChanged, AddressOf User_PropertyChanged
LoadRestrictedReports()
Else
CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
End If
Dim loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows))
ProductsGrid.ItemsSource = loadProducts.Entities
End Sub
Add an event handler for the PropertyChanged event that calls LoadReports if the property DefaultRows has changed.
Private Sub User_PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
If (e.PropertyName = "DefaultRows") Then
LoadReports()
End If
End Sub
Add event handlers for the LoggedIn and LoggedOut events that either load or hide data based on the change in the user authentication credentials.
Private Sub Authentication_LoggedIn(ByVal sender As Object, ByVal e As Authentication.AuthenticationEventArgs)
LoadReports()
End Sub
Private Sub Authentication_LoggedOut(ByVal sender As Object, ByVal e As Authentication.AuthenticationEventArgs)
CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
End Sub
Add the following code to the constructor. This code loads the handlers and calls LoadReports.
Public Sub New()
InitializeComponent()
Me.Title = ApplicationStrings.ReportsPageTitle
AddHandler WebContext.Current.Authentication.LoggedIn, AddressOf Authentication_LoggedIn
AddHandler WebContext.Current.Authentication.LoggedOut, AddressOf Authentication_LoggedOut
LoadReports()
End Sub
publicReports(){InitializeComponent();this.Title=ApplicationStrings.ReportsPageTitle; WebContext.Current.Authentication.LoggedIn += new System.EventHandler<AuthenticationEventArgs>(Authentication_LoggedIn);
WebContext.Current.Authentication.LoggedOut += new System.EventHandler<AuthenticationEventArgs>(Authentication_LoggedOut);
LoadReports();}
The following shows the complete code file.
Imports System.Windows.Navigation
Imports System.Windows.Controls
Imports OpenRiaServices.Client
Imports OpenRiaServices.Client.Authentication
Imports ExampleBusinessApplication.Web
Partial Public Class Reports
Inherits Page
Private context As New AdventureWorksDomainContext
Private numberOfRows As Integer = 10
Public Sub New()
InitializeComponent()
Me.Title = ApplicationStrings.ReportsPageTitle
AddHandler WebContext.Current.Authentication.LoggedIn, AddressOf Authentication_LoggedIn
AddHandler WebContext.Current.Authentication.LoggedOut, AddressOf Authentication_LoggedOut
LoadReports()
End Sub
Private Sub LoadReports()
If (WebContext.Current.User.IsAuthenticated) Then
numberOfRows = WebContext.Current.User.DefaultRows
AddHandler WebContext.Current.User.PropertyChanged, AddressOf User_PropertyChanged
LoadRestrictedReports()
Else
CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
End If
Dim loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows))
ProductsGrid.ItemsSource = loadProducts.Entities
End Sub
Private Sub LoadRestrictedReports()
Dim loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows))
SalesOrdersGrid.ItemsSource = loadSales.Entities
SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible
If (WebContext.Current.User.IsInRole("Managers")) Then
Dim loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows))
CustomersGrid.ItemsSource = loadCustomers.Entities
CustomersGrid.Visibility = System.Windows.Visibility.Visible
Else
CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
End If
End Sub
Private Sub User_PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
If (e.PropertyName = "DefaultRows") Then
LoadReports()
End If
End Sub
Private Sub Authentication_LoggedIn(ByVal sender As Object, ByVal e As Authentication.AuthenticationEventArgs)
LoadReports()
End Sub
Private Sub Authentication_LoggedOut(ByVal sender As Object, ByVal e As Authentication.AuthenticationEventArgs)
CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
End Sub
End Class
Notice that when you are not logged in, only the products table is displayed on the Reports page.
Click the login link and log in as SalesUser.
Notice that the tables for products and sales orders are displayed.
Log out and log in as CustomerManager.
Notice that the tables for products, sales orders, and customers are displayed.
Close the Web browser.
You can allow users to edit the DefaultRows profile property by adding child window. When the value is changed, you call the SaveUser method to save the value in the data source. You retrieve the current value through the properties on the User object of the current WebContext instance.
Add a window for setting profile property
In the client project, add a new item to the Views folder.
Select the Silverlight Child Window template and name the child window ProfileWindow.xaml.
Click the Add button.
In the ProfileWindow.xaml file, add the following XAML after the Grid.RowDefinitions element to include a ComboBox for selecting the number of rows to display in the reports.
Set the Title property on the ChildWindow to Select Preferences.
In the ProfileWindow.xaml.cs or ProfileWindow.xaml.vb file, add the following code to retrieve and set the profile property.
Imports System.Windows.Controls
Imports System.Windows
Partial Public Class ProfileWindow
Inherits ChildWindow
Public Sub New()
InitializeComponent()
Dim userDefaultRows = WebContext.Current.User.DefaultRows.ToString()
For Each cbi As ComboBoxItem In defaultRows.Items
If (cbi.Content.ToString() = userDefaultRows) Then
defaultRows.SelectedItem = cbi
Exit For
End If
Next
End Sub
Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click
Dim newSelection = Integer.Parse(defaultRows.SelectionBoxItem.ToString())
If (newSelection <> WebContext.Current.User.DefaultRows) Then
WebContext.Current.User.DefaultRows = newSelection
WebContext.Current.Authentication.SaveUser(True)
End If
Me.DialogResult = True
End Sub
Private Sub CancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles CancelButton.Click
Me.DialogResult = False
End Sub
End Class
In the LoginStatus.xaml.cs or LoginStatus.xaml.vb file, add the following click event handler for the settings link.
Private Sub SettingsButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim settingsWindow As New ProfileWindow
settingsWindow.Show()
End Sub
privatevoidSettingsButton_Click(object sender,RoutedEventArgs e){ ExampleBusinessApplication.Views.ProfileWindow settingsWindow = new ExampleBusinessApplication.Views.ProfileWindow();
settingsWindow.Show();}
Run the solution.
Log in as either CustomerManager or SalesUser and notice the login status bar now includes a link for settings.
Click the settings link and set the default number of rows to show for the reports.
Open the Reports page and notice that the DataGrid now contains the number of rows that you selected.