Tuesday, October 24, 2006

Active Directory and User Roles

The WinForms app I'm working on has two modes: edit and read-only. It's all or nothing: either you can edit everything or you can edit nothing. If you're not allowed even read-only access you can't run the app.

So I am faced with the challenge, familiar to many of you, of providing read-only vs. edit access in a nice, clean way. There's an article by Irena Kennedy called Granular Role Based Security Without a Line of Code: Attribute-Based Authorization that I really like. It uses attributes to indicate default values to be set for object properties based on their authorization to specific actions in Authorization Manager. I like this idea. I could put something in a post-InitializeComponent method in my form base class that ran through the controls declared in its descendant and set default properties based on the role membership of the user.

But I'm using AD and WinForms. So how do we shoehorn AD groups into the Identity, Principal, and Role model? The user's groups seem obvious choices for roles, but the WindowsIdentity object already has its own collection of groups. I thought about loading the AD groups of each user into the Roles collection of a GenericPrincipal, but it seems counter-intuitive to replace an authenticated WindowsPrincipal with an unauthenticated GenericPrincipal.

So I was thinking about turning around and heading in the opposite direction. Maybe I should create Attributes that didn't pretend to be other than AD-specific: IsInGroupAttribute(string groupName, string propertyName, object defaultValue) and maybe NotInGroupAttribute(string groupName, string propertyName, object defaultValue). When processed by the initialization routine we would just do an AD lookup (cached, of course) and check group membership.

Go on, tell me why this is a bad idea.

Thursday, October 05, 2006

To Refactor or not to Refactor

I created a WinForms combo box that restricts what options you can choose. This is for the situation where only a few of the options are valid choices at this point in your process, but the data you are binding to includes other values that must appear in the combo box to avoid binding errors. For example, it might be possible to manually change the status of any order only to Canceled or Hold, but the application allows the users to query and view orders of any status.

(ETA: we're assuming in-place editing, here. The problem goes away if you have separate read-only "Status" and write-only "New Status" controls. I'm not sure if you can do that with databinding alone.)

I created a stand-alone combo box version of this -- the RestrictedComboBox -- and also a DataGridView version. After a day or so of work it became obvious that rather than building column, cell, and editing control classes on top of the RestrictedComboBox, it made more sense to subclass the ComboBox versions of these classes.

This means that the RestrictedComboBox and DataGridViewRestrictedComboBoxEditingControl code files are made up of almost identical code. Both of them are descended from the ComboBox (directly in the case of the RestrictedComboBox, more distantly in the case of the other) and do their thing by interfering with combo box item selection and drawing. I'm toying with refactoring the common logic into a helper class. What do you think -- go to the trouble or not go to the trouble?