Mad Props!

Omniscience is just a Google-search away.

Login

You're reading Mabsterama, the weblog of Matt Hamilton. Enjoy your stay.

RoleProvider RolePropertyDescriptor

So this next piece of the RoleProvider puzzle is a class which describes a custom property on our RoleProvider.

As you recall, RoleProvider lets us add new "roles" by simply adding items to a dictionary property on the class itself. What we want to do is turn each of those "roles" into properties on the class, which we can bind to using normal WinForms or WPF databinding.

So each of the roles is a bool property, representing whether the current user is a member of that particular role.

Here's the class:

public class RolePropertyDescriptor : PropertyDescriptor { public RolePropertyDescriptor(string name) : base(name, null) { } public override bool IsReadOnly { get { return true; } } public override void SetValue(object component, object value) { } public override bool CanResetValue(object component) { return false; } public override void ResetValue(object component) { } public override bool ShouldSerializeValue(object component) { return false; } public override Type ComponentType { get { return typeof(RoleProvider); } } public override Type PropertyType { get { return typeof(bool); } } public override object GetValue(object component) { RoleProvider rp = component as RoleProvider; if (rp == null) return null; return rp.IsInRole(Name); } } 

We inherit from the abstract PropertyDescriptor class, and override only the methods we have to. This is a read-only property, and (as you can see from the "PropertyType" override) it's of boolean type.

Note that the "GetValue" override calls back to our "IsInRole" method from the previous post, using the name of the current property, to determine if the user is in this role.

The next step is to define a class which tells the framework to add one of these properties to the RoleProvider class for each role we've defined. That's the next post.

RoleProvider Class

Here's part one of how I put together our "RoleProvider" class (whose name could conceivably change down the track), the usage of which I described here.

In this post I'll show you the class itself. Then in the next couple of days I'll post the helper classes that tell the Windows Forms and WPF data binding engine about the "extra properties" on the class.

Without further ado, the class:

public class RoleProvider { public RoleProvider() { WindowsIdentity id = WindowsIdentity.GetCurrent(); _principal = new WindowsPrincipal(id); _roles["Administrators"] = new string[] { "Domain Admins" }; } private Dictionary<string, IEnumerable<string>> _roles = new Dictionary<string, IEnumerable<string>>(); private WindowsPrincipal _principal; [Browsable(false)] public Dictionary<string, IEnumerable<string>> Roles { get { return _roles; } } public bool IsInRole(string role) { foreach (string grp in _roles[role]) { if (_principal.IsInRole(grp)) return true; } return false; } } 

So there's not much to look at here. I'm picking up the currently-logged-in user with the WindowsIdentity.GetCurrent() call, and I've set up a dictionary of role names, each of which maps to a collection of strings (representing the AD groups that that role maps to).

For simplicity I've also added an "IsInRole" method, which iterates through all the groups defined in a role and returns true if the user is a member of at least one of them.

Next up I'll post the PropertyDescriptor descendant class which describes the boolean property for each role you add to the Roles dictionary.

CustomTypeDescriptors and WPF

In some of our existing applications here at work, we use a little trick to determine whether a user has permission to click on a button or menu item. We use Active Directory to determine if the current user is in a certain group, and enable/disable the control based on that.

Today, Pete and I were discussing whether there might be a "binding oriented" (as Paul would say) way to do this, so that our newer applications (particularly WPF-based ones) could declaratively determine a control's IsEnabled state.

At first we tossed up the idea of simply building a class for each application with public properties for each potential role. For example, a public boolean property called "IsDepartmentalManager" or something. You could then bind the IsEnabled property of your button directly to that. That would work, but it means a lot of repetitive code - defining a new class with a bunch of new properties each time you created a new app.

I suggested that maybe there's a way to do it so that you simply define the roles you want as strings, and add them to a collection on a class we define only once. So I started reading about CustomTypeDescriptor and its related classes. These classes let you "fool" the binding framework into seeing properties on your class that don't really exist. For example, it's how a DataTable tells the DataGrid it's bound to what columns to show.

The end result is a class that (for now) I have dubbed "RoleProvider". It has a "Roles" property which represents the list of roles you want to define in your application, and the AD groups those roles correspond to. Here's an example. First, some XAML:

<Window x:Class="WindowsApplication2.Window1"  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  Title="WindowsApplication2" Height="300" Width="300"  > <Window.Resources> <ObjectDataProvider x:Key="RoleProvider" /> </Window.Resources> <Grid> <Button IsEnabled="{Binding Source={StaticResource RoleProvider},Path=IT}"  Content="Only IT Can Click Here!"/> </Grid> </Window>

Pretty simple. One button whose IsEnabled property is bound to an ObjectDataProvider and a property called "IT". The theory is that if you're in that "IT" role, you're allowed to click the button. Now some C#:

public partial class Window1 : System.Windows.Window { public Window1() { InitializeComponent(); RoleProvider rp = new Qaf.Security.RoleProvider(); rp.Roles["IT"] = new string[] { "GR-COR-IT" }; ObjectDataProvider odp = Resources["RoleProvider"] as ObjectDataProvider; odp.ObjectInstance = rp; } } 

So we create a new RoleProvider, add an "IT" role which is defined as a single AD Group ("GR-COR-IT"), and then point our ObjectDataProvider to it.

You'll have to trust me when I say that the application works as advertised. If you're not in the GR-COR-IT group (and hence not in our "IT" role), the button is disabled.

I'll put together a post or two about the implementation of RoleProvider next week. Stay tuned!

Pssst! Wanna Buy a DVD Player?

We're selling our old Denon DVD-1600 player on eBay, if you're interested. We're not using it nowadays since we updated to the 1930.

Check out the auction here.

LINQ to AWDNUG

September's meeting of the Albury/Wodonga .NET User Group was all about new features in Visual Studio 2008 and the .NET framework 3.5 - specifically LINQ. Andrew led us through beta 2 of VS2008, giving a great demo of automatic properties, object initialization, extension methods, type inference, anonymous types ... the works!

Of interest to me was the LINQ to SQL stuff. The visual designer for LINQ classes looks really solid. It felt like you'd be much more confident messing with it than you would the typed DataSet designer. I don't know about you, dear reader, but I always have livestock ready to sacrifice to the gods of Visual Studio when I have to modify a typed DataSet. I never quite know whether the changes will have an adverse affect.

One thing that I still don't fully understand with LINQ to SQL is how the the DataContext object (which is essentially your connection back to the database, tracking changes to objects) will work across process boundaries. For example, if I return a collection of LINQ objects from a web service, and the client modifies them and gives them back to me ... how does my DataContext know if they're new rows or modified rows? That was something that DataSets handled quite nicely. Some reading tells me that there's an "attach" method on the DataContext that lets me play with objects that weren't originally queried from that DataContext, so that looks like they've already thought of it. More research required before I fully grok it all.

Still no agenda for next month's meeting. If you have an idea for a presentation, please let us know! I'm thinking that we could probably go back to the two-presentation format to lessen the pressure on presenters.

It's Alive! ALIVE!!!

And on the third day, thanks to hardware guru Mike from work, my computer rose from the dead!!!

It turns out it was just the power supply. I nipped into town at lunchtime and picked up a new Antec 500W PSU and now we're all going again!

So many thanks to Mike for all his help. My withdrawal symptoms are already starting to fade!

Dead PC

Gaaah! My PC has died! It's toast!

Got home from seeing Ratatouille this afternoon, and my DVD drive had locked up. I had left a DVD burning using Nero, and when I came back to it, Nero had errored, and I could no longer eject the DVD. So I attempted a restart of Windows.

Problem is, Windows didn't restart. It sat there at a black screen after shutting down.

So I powered down the PC, waited a few seconds, and turned it back on again.

All the fans turn on. The LEDs on the power supply and wi-fi card turn on. I'm pretty sure I can even hear the hard drives spin up. But no "beep". The thing just won't boot.

So maybe the motherboard is fried. I'm not sure ... never been much of a hardware guy unfortunately. I'm laptop-bound until I can get a local shop to take a look at it. What a bummer! It's only a little over two years old! I mean ... I have been looking at the price of new PCs ... but not seriously I swear!!!

Windows Live Mail - A Nice Touch

I upgraded yesterday to the new betas from Windows Live, including the latest version of Windows Live Mail. There were the usual batch of bug fixes, performance tweaks and UI changes, but one little addition caught my eye this morning:

When you select the "Junk E-mail" folder (usually to check for false-positives or just to empty the folder), you no longer get a "preview pane". That means that you don't have to read spam just to delete it.

Since a lot of spam relies on you actually seeing the message (with embedded images etc), I thought this was a really nice touch. Now you have to deliberately open spam email before you see the contents, and you can delete it unread.

If you're interested in checking out the new Live betas, you can install a whole bunch of 'em in one hit (you get to choose which ones, of course) from here. Take a look!

LINQ to Text

This week's episode of Hanselminutes was all about LINQ to XML - a feature of the upcoming 3.5 release of the .NET framework and associated compilers. You can read more about it here, if you aren't already all over it.

Scott has talked about the concept of "languages within languages" before, and he mentioned it again in this episode when Carl brought up XPath. The idea is that you have to context switch to code XPath or, say, SQL from C# or VB because it's essentially a whole different language contained within a literal string in your program. Having to change gears half way through writing a method is not a good way to code, and the fact that these "embedded languages" aren't compile-time checked makes your software more open to runtime errors (or worse yet, problems that don't error but cause data corruption in some way).

This is where LINQ comes to the rescue, because most of these mini-languages are query-oriented, and LINQ brings the query syntax into C# or VB. So no more context switching.

This got me thinking about a third query-oriented mini-language: Regular Expressions. We use them to "query" strings - searching for patterns within text. Could there conceivably be a "LINQ to Text" that does away with RegEx? What would the syntax look like?

 var emailAddresses = from w in myString.Words() where w.Contains("@") select w;
I don't know whether this would make it any easier, but I do know that RegEx is pretty hard. Maybe LINQ to Text could solve that problem.

Quick Visual Studio Keyboard Macros

Did you know that you can record keyboard macros in Visual Studio?

Let's say you've got thirty lines of similar code (maybe adding literal strings to a list or something), and you want to make a similar change (say, upper-case the first character of the string) to every line. The lines don't quite line up, so you can't just do a column-select (Alt+mouse-drag) to grab them all. What do you do?

First, you hit "Ctrl+Shift+R" to begin recording a macro.

Next, you hit "Home" and then Ctrl+Right-Arrow until you're at the character you want to delete. You hit "Delete" and then down-arrow to move to the next line.

Now hit "Ctrl+Shift+R" to stop recording.

Now you're all set to play your macro back using Ctrl+Shift+P! It'll play those keystrokes back, which will delete the character the same number of words into the line as the last, and then move to the next line!

Keyboard macros are really handy for quick, repetitive jobs, and knowing about Ctrl+Shift+R and Ctrl+Shift+P just makes them so easy!