Showing posts with label microsoft. Show all posts
Showing posts with label microsoft. Show all posts

Friday, August 15, 2008

Active Directory Role Provider


I've been using the ActiveDirectoryMembershipProvider to allow my users to login to a custom ASP.Net site with their AD credentials and it is pretty straight forward. Recently I needed to add more granularity to who can view various parts of the site. I wanted to take advantage of our existing AD groups so I assumed there would be something like an ActiveDirectoryRoleProvider as well. After a little searching, it became clear that wasn't the case, so I decided to roll my own.

Creating a custom role provider is pretty easy, all you have to do is create a new class and inherit RoleProvider:


public class ActiveDirectoryRoleProvider : RoleProvider
{}


You will have to create stubs for all the inherited members. We only need to implement a couple of them to get basic role membership checking. We need to get our AD configuration information out of the Web.Config values, so we'll create a few properties and override the Initialize method:



private string ConnectionStringName { get; set; }
private string ConnectionUsername { get; set; }
private string ConnectionPassword { get; set; }
private string AttributeMapUsername { get; set; }

public override void Initialize(string name, NameValueCollection config)
{
ConnectionStringName = config["connectionStringName"];
ConnectionUsername = config["connectionUsername"];
ConnectionPassword = config["connectionPassword"];
AttributeMapUsername = config["attributeMapUsername"];

base.Initialize(name, config);
}


Now we'll override GetRolesForUser which is the bulk of our implementation. We use the DirectorySearcher class in System.DirectoryServices to query AD for the passed username. We then pull the memberOf property from that user and extract the CN component for each entry as the role:



public override string[] GetRolesForUser(string username)
{
var allRoles = new List<string>();

var root = new DirectoryEntry(WebConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString, ConnectionUsername, ConnectionPassword);

var searcher = new DirectorySearcher(root, string.Format(CultureInfo.InvariantCulture, "(&(objectClass=user)({0}={1}))", AttributeMapUsername, username));
searcher.PropertiesToLoad.Add("memberOf");

SearchResult result = searcher.FindOne();

if (result != null && !string.IsNullOrEmpty(result.Path))
{
DirectoryEntry user = result.GetDirectoryEntry();

PropertyValueCollection groups = user.Properties["memberOf"];

foreach (string path in groups)
{
string[] parts = path.Split(',');

if (parts.Length > 0)
{
foreach (string part in parts)
{
string[] p = part.Split('=');

if (p[0].Equals("cn", StringComparison.OrdinalIgnoreCase))
{
allRoles.Add(p[1]);
}
}
}
}
}

return allRoles.ToArray();
}


And finally we need to override IsUserInRole so we can easily check for role membership in code:



public override bool IsUserInRole(string username, string roleName)
{
string[] roles = GetRolesForUser(username);

foreach (string role in roles)
{
if (role.Equals(roleName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}

return false;
}


Here's the the full code (minus the unimplemented inherited methods):



public class ActiveDirectoryRoleProvider : RoleProvider
{
private string ConnectionStringName { get; set; }
private string ConnectionUsername { get; set; }
private string ConnectionPassword { get; set; }
private string AttributeMapUsername { get; set; }

public override void Initialize(string name, NameValueCollection config)
{
ConnectionStringName = config["connectionStringName"];
ConnectionUsername = config["connectionUsername"];
ConnectionPassword = config["connectionPassword"];
AttributeMapUsername = config["attributeMapUsername"];

base.Initialize(name, config);
}

public override bool IsUserInRole(string username, string roleName)
{
string[] roles = GetRolesForUser(username);

foreach (string role in roles)
{
if (role.Equals(roleName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}

return false;
}

public override string[] GetRolesForUser(string username)
{
var allRoles = new List<string>();

var root = new DirectoryEntry(WebConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString, ConnectionUsername, ConnectionPassword);

var searcher = new DirectorySearcher(root, string.Format(CultureInfo.InvariantCulture, "(&(objectClass=user)({0}={1}))", AttributeMapUsername, username));
searcher.PropertiesToLoad.Add("memberOf");

SearchResult result = searcher.FindOne();

if (result != null && !string.IsNullOrEmpty(result.Path))
{
DirectoryEntry user = result.GetDirectoryEntry();

PropertyValueCollection groups = user.Properties["memberOf"];

foreach (string path in groups)
{
string[] parts = path.Split(',');

if (parts.Length > 0)
{
foreach (string part in parts)
{
string[] p = part.Split('=');

if (p[0].Equals("cn", StringComparison.OrdinalIgnoreCase))
{
allRoles.Add(p[1]);
}
}
}
}
}

return allRoles.ToArray();
}
}


Add the role provider to your Web.Config:



<system.web>
<roleManager enabled="true" defaultProvider="ADRoleProvider" cacheRolesInCookie="true" cookieName=".ASPXROLES" cookiePath="/"
cookieTimeout="30" cookieRequireSSL="false" cookieSlidingExpiration="true" createPersistentCookie="false" cookieProtection="All">
<providers>
<clear />
<add name="ActiveDirectoryRoleProvider" connectionStringName="ADConnectionString" connectionUsername="username"
connectionPassword="password" attributeMapUsername="sAMAccountName" type="ActiveDirectoryRoleProvider" />
</providers>
</roleManager>
</system.web>


You can then check the roles of your user in code like so:



Roles.IsUserInRole("My Group")


Or control access to entire directories via the Web.Config:



<location path="RestrictedSubDirectory">
<system.web>
<authorization>
<allow roles="My Group"/>
<deny users="*" />
</authorization>
</system.web>
</location>

Tuesday, April 29, 2008

New Date and Time Data Types in SQL Server 2008

I am delinquent in the one-a-week challenge post for last week, and on top of that this is going to be a pretty lazy one, as I'm just going to refer to a good article I just found.

My current client has taken to early adopting the latest SQL Server 2008 CTP. One of my favorite new features are the enhanced date and time data types. Dinesh Asanka has a good summary of these new types over at SQL Server Performance and you should really just go check out his article.

My favorite additions are the DATE and TIME data types, how many times have you wanted to just specify a date or time portion of a value and had to do some sort of crazy truncation or logic in code to keep it straight? Well no longer.

Another great addition is the DATETIME2 data type, though its not going to win any awards for clever naming, the gist of it is that you now can specify the millisecond resolution of your DATETIME, previously it was defaulted to 3 decimal places, now you can specify from 0 to 7.

Sunday, March 9, 2008

Back from Mix '08

I attended the Mix '08 conference in lovely Las Vegas last week. Mix is Microsoft's conference for discussing and launching web technologies held at the Venician Hotel and Casino.

The opening keynote by Scott Guthrie (who's blog I recommend subscribing to) was pretty good. There were two primary announcements:

Silverlight 2

At Mix '07 Microsoft launched Silverlight, their competitive technology to Adobe's Flash. This year they launched Sliverlight 2 Beta 1 during the keynote. Not having ever worked with Silverlight 1 I cannot speak with much authority on the subject. I can say that the demos were impressive, especially the Hard Rock Memorabilia Deep Zoom demo. In addition, my colleague Than, who has been working with Silverlight 1 for the last year, informs me that the 2.0 release is an enormous improvement on the prior version.

Internet Explorer 8

Also during the keynote Microsoft launched Internet Explorer 8 beta. I did not expect to care much about this announcement considering my primary browser has been Firefox for years now, even before I made the move to the Mac. However, my tune quickly changed as they demoed the new version. Having been a web developer for many years now, I have come to accept the constant battle with browser compatibility. I was not prepared for their first demo to involve a basic CSS styled site, being shown in Firefox. The site looked fine, and then the speaker switched to Safari and again, it looked just fine. Then he switched to IE 7, and a giant red square appeared on the page, obviously highlighting a standards compatibility issue that IE has had for ages. And finally, he switch to IE 8, and the sight looked just like it did in Firefox and Safari. I am truly impressed with Microsoft's acceptance of the fact that yes, they are not standards compliant, and one of their major goals in IE 8 is to fix that. This feels like a complete 180 from the Microsoft of the past, and it is a welcome change in my opinion. The rest of the IE 8 demo was pretty good as well. They showed off built in development tools that should make anyone's life much easier as we all continue to build sites that must work across various browsers.

I attended a couple of session that I found to surprising as well.

Developing Data Driven Web Applications Using ASP.Net Dynamic Data

I walked into this session not really knowing what I was about to see. The presentation was about how Dynamic Data allows you to generate a UI scaffolding based on your data model for instant CRUD based UIs, sound familiar? It should, this is clearly Microsoft's answer to Ruby on Rails, or at least part of it. What they demoed was fairly good and comprehensive, and moreover, it wasn't being pushed as the end-all-be-all of frameworks as MS is known to do, but rather as yet another option in your ASP.Net arsenal. This again, is not not something I would have expected to hear out of the Microsoft of the past.

The ASP.Net MVC Framework

By far my favorite presentation was Scott Hanselman's session on the new MVC framework for ASP.Net. This was true for a couple of reasons, the first being that Scott is a truly engaging and entertaining speaker, which is a pretty impressive feat when you consider the content and audience. Secondly I enjoyed it because again, this framework was not being pushed as a replacement of anything, but rather another alternative. MVC makes up the second piece of competing with the Ruby on Rails framework, with the model-view-controller architecture becoming baked into the tools. It is still in its early stages, but looks very promising. I have been doing a lot of iPhone web application development lately, and one of my biggest issues is keeping may page size small. Due to the MVC framework's natural lack of your standard viewstate/postback architecture, it seems to me that the views you generate can be much more easily optimized for a device like the iPhone that can handle full HTML, but may sometimes be limited by bandwidth.

Overall I had a blast at Mix. Thanks Microsoft for hosting the party at Tao in the Venician, I can't imagine what the bar bill looked like for that.