Working on a little XAF project, I recently had the need (a common one) to support the concept of objects being owned by certain users. When a user logs in, he or she should only see the objects owned by that user. This needed to apply to both the list views and the data available in reports.
In the end, this was fairly easy to do. I thought I’d share some notes about how I accomplished this, as some of the resources online are a bit outdated, or don’t lay all of these aspects out together.
Note that there is currently a request to support this sort of scenario directly in XAF’s security system. You can track the issue here.
The first thing to do is define the business object itself (we’ll called it OwnedObject). The only thing particularly interesting here is defining a property to reference the object’s owner, and initializing that value:
public class OwnedObject : BaseObject { public OwnedObject(Session session) : base(session) { } public override void AfterConstruction() { base.AfterConstruction(); if (Session.IsNewObject(this)) { _ObjectOwner = Session.GetObjectByKey<SimpleUser>((Guid)SecuritySystem.CurrentUserId); } } private SimpleUser _ObjectOwner; public SimpleUser ObjectOwner { get { return _ObjectOwner; } set { SetPropertyValue("ObjectOwner", ref _ObjectOwner, value); } } }
One way to filter list views by this ObjectOwner property is discussed here. Open up the XAFML file for the platform agnostic Module project. Navigate to the OwnedObject_ListView node under Views. Set the Criteria to ObjectOwner.Oid = '@CurrentUserID'
. Here, ObjectOwner is the property defined on the OwnedObject class, and @CurrentUserID is expanded at runtime to contain the current user’s ID.
Set the same Criteria property on the OwnedObject_LookupListView node as well, and run your application. Both the WinForms and ASP.NET applications should now only show the objects created by the current user, which is automatically set when the object is created. Note that you can also accomplish the same thing with a View Controller.
You can hide the ObjectOwner property at runtime by deleting its respective items under the DetailView, ListView, and LookupListView nodes in the model editor. Also note that this can easily be applied to reports as well, so that a user only sees his or her reports. Simply descend a new object from ReportData, and use your OwnedReport as the data type for the report views.
This is all pretty cool with minimal code, but one issue that needs to be addressed is the data shown in reports. If a user were to create and run a report on OwnedObjects, they’d see all objects regardless of owner. This is also fairly easy to fix, but requires two solutions, one for the WinForms project and one for the ASP.NET project.
The solution for the WinForms project is discussed here. To the WinForms module, add a new View Controller. You’ll also need to add references to ExpressApp.Reports, ExpressApp.Reports.Win, XtraReports, and XtraReports.Design. Add the following code to your controller:
protected override void OnActivated() { base.OnActivated(); XtraReport.FilterControlProperties += XtraReport_FilterControlProperties; Frame.GetController<WinReportServiceController>().CustomShowPreview += ViewController1_CustomShowPreview; Frame.GetController<WinReportServiceController>().CustomShowDesignForm += ViewController1_CustomShowDesignForm; } void XtraReport_FilterControlProperties(object sender, FilterControlPropertiesEventArgs e) { if (e.Control is XtraReport) { e.Properties.Remove("FilterString"); } } void ViewController1_CustomShowDesignForm(object sender, CustomShowDesignFormEventArgs e) { e.Report.FilterString = CriteriaOperator.Parse("ObjectOwner.Oid=?", SecuritySystem.CurrentUserId).ToString(); } void ViewController1_CustomShowPreview(object sender, CustomShowPreviewEventArgs e) { e.Report.FilterString = CriteriaOperator.Parse("ObjectOwner.Oid=?", SecuritySystem.CurrentUserId).ToString(); } protected override void OnDeactivating() { base.OnDeactivating(); Frame.GetController<WinReportServiceController>().CustomShowPreview -= ViewController1_CustomShowPreview; Frame.GetController<WinReportServiceController>().CustomShowDesignForm -= ViewController1_CustomShowDesignForm; }
Basically, when the controller is activated, it will add two event handlers to the report service controller for WinForms applications – one for when a preview is shown, and one for when the report is designed. These events force a criteria that only shows objects owned by the current user. The FilterControlProperties event hides the FilterString property.
I had to figure out the ASP.NET portion on my own, but it wasn’t hard. Based on the WinForms solution above, I knew to look at the WebReportServiceController. Turns out you can simply descend from that class and override ShowPreviewCore to filter the data.
For the ASP.NET module, add a new class. Descend the class from WebReportServiceController and add the following code:
protected override void ShowPreviewCore(IReportData reportData, CriteriaOperator criteria) { criteria = new BinaryOperator("ObjectOwner.Oid", SecuritySystem.CurrentUserId); base.ShowPreviewCore(reportData, criteria); }
You’ll also need to add references to ExpressApp.Reports, ExpressApp.Reports.Web, XtraReports, and XtraReports.Web. This does essentially the same thing as the WinForms solution, overriding the preview of the report to force a filter on the object’s owner.
With those two changes made, you can now run both the WinForms and ASP.NET XAF applications, create and run reports, and you will only be able to see your own objects.
You can download a sample that demonstrates all this here.
