Thursday, May 22, 2008

Querying Active Directory groups via LDAP in ASP.NET

The RoleManager in .NET is great for SQL-based auth, but is really lacking when it comes to Windows auth. I needed to get a list of all users in a role, and was excited to find that the following method exists:

Roles.GetUsersInRole("RoleName");
By the way, to use RoleManager, add the following code in your Web.Config file within the System.Web tags:


<authentication mode="Windows" />
<roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider" />
Perfect, except that you get the following error when using it with Windows auth:
"The configured Role Provider (WindowsTokenRoleProvider) relies upon Windows authentication to determine the groups that the user is allowed to be a member of. ASP.NET Role Manager cannot be used to manage Windows users and groups. Please use the SQLRoleProvider if you would like to support custom user/role assignment."
Rather lame. I don't want to add/edit/delete users and groups - just look up what users are in a group. In any case, I ended up querying this info from Active Directory with LDAP. I made a static class called "AuthUtils" and first added the following method:


using System.Collections.Generic;
using System.DirectoryServices;
...

public static List<string> GetUsersInGroup(string LDAPAddress) {
List<string> groupMembers = new List();
DirectoryEntry group = new DirectoryEntry(LDAPAddress);
foreach(object dn in group.Properties["member"]) {
string[] parsedName = dn.ToString().Replace("CN=", String.Empty).Split(',');
groupMembers.Add(parsedName[0]);
}

return groupMembers;
}
which is callable with the following:


string LDAPGroupAddress = "LDAP://domain.com/CN=GroupName,CN=Users,DC=domain,DC=com";
List<string> groupMembers = AuthUtils.GetUsersInGroup(LDAPGroupAddress);
The above method strips out the CN (Canonical Name) tags from the results, and returns a list of user name strings.

I also built another quick method in AuthUtils to determine if a user is in a role, which also depends on the "GetUsersInGroup" method that was just built:


public static bool IsInRole(string LDAPAddress, string user) {
List<string> groupMembers = GetUsersInGroup(LDAPAddress);
return groupMembers.Contains(user);
}
This simply gets the group members as above, and uses the handy "Contains" method that's available in generic lists to return a true or false.

There is a User.IsInRole that works with regular NTLM auth, but for some reason it seems a bit flaky with an Active Directory back end. The method I've created above has always worked for me so far. I'm in the process of figuring out what the issue is with the regular User.IsInRole("RoleName") method, as it's bothersome that this doesn't work consistently.

UPDATE: Roles.IsUserInRole("RoleName") works fine with AD (vs. using User.IsInRole("RoleName"), as long as you're using IIS. It doesn't seem to work well with Visual Studio's built-in Web server (it always caches the role info).

Tuesday, May 20, 2008

Free J2EE online course

The next run of the J2EE Programming course in the excellent "Java with Passion" series starts on July 1st. This is all put together by Sang Shin, a technology architect at Sun Microsystems.

More details here:
http://www.javapassion.com/j2ee/

I've just signed up.

Wednesday, May 14, 2008

Java --> .NET rough equivalents

To help me understand Java concepts better, I've made a comparison to the rough equivalents in .NET.

Here's the list so far:
  • Java ~= C# or VB.NET
  • J2EE/JEE (Java Enterprise Edition) ~= .NET framework
  • Swing ~= Windows Forms
  • AWT ~= VB6 Forms?
  • Beans Binding ~= data binding
  • JPA (Java Persistence API) ~= ORM via ADO.NET Entity Framework / LINQ
  • EJBQL/JPQL (Enterprise JavaBeans Query Language/Java Persistence Query Language) ~= LINQ
  • JDO ~= ADO.NET
  • JSF (Java ServerFaces) ~= ASP.NET
  • JSP (Java Server Pages) ~= plain ASP
  • Hibernate, JUnit, Spring all have direct .NET equivalents (NHibernate, NUnit, Spring.NET)
I'm still getting a grasp on a lot of this stuff, so please let me know if I've misrepresented anything. I'll also add to the list as I think of more things.

UPDATE: Paul Sasik posted a question about .NET/Java comparisons at StackOverflow.com, and got a lot of great responses, which update and expand this list considerably (see http://stackoverflow.com/questions/2526024/analogues-of-java-and-net-technologies-frameworks). Thanks for linking to my initial blog post, Paul.

Installing SwingX in NetBeans 6.1

SwingX adds some really nice UI and other improvements to Swing. It's sponsored by Sun, and may be released in a future version of Java, but appears stable enough to use in projects now.

There's a tutorial on how to set it up, but it's missing some important content, so here's how I did it:

1. Download and unzip from http://swinglabs.org/downloads.jsp
2. Place swingx-x.x.x.jar file (from swingx/dist/ folder) in a place that you won't delete it later.
3. Open your project in NetBeans, right-click on "libraries", and choose "Add Library..."
4. Click on "Create...", give it a name (i.e. "SwingX"), and select "Class Libraries".
5. On the next dialog box, under the "Classpath" tab, click "Add JAR/Folder..." and locate the swingx-x.x.x.jar file from step 2.
6. Add the new library to your project.
7. To get the visual items in the palette, go to a jFrame or other class that has a design view, right-click anywhere on the Palette view, and select "Palette Manager...".
8. Click on "New Category..." and type "SwingX" (or whatever you want to call it).
9. Click on "Add from JAR...".
10. Find your swingx.x.x.x.jar file again, and on the next screen, select all the components you want to be displayed.
11. Finally, select the category folder you just created in step 8.

Tuesday, May 13, 2008

Working with SQL Server in NetBeans 6.1

The JDBC-ODBC bridge wasn't recognizing any of the primary keys I had set up in SQL Server. After lots of head banging, here's the solution I came up with:

1. Download the appropriate driver. SQL Server 2000 requires the SQL Server 2000 JDBC driver (http://www.microsoft.com/downloads/details.aspx?FamilyId=07287B11-0502-461A-B138-2AA54BFDC03A&displaylang=en).
SQL Server 2005 download: (http://www.microsoft.com/downloads/details.aspx?familyid=C47053EB-3B64-4794-950D-81E1EC91C1BA&displaylang=en)
3. After installing, right-click on "Libraries" in your project, and choose "Add Library...". Next, give it a name (i.e. SQLServer2000), and select "Class Libraries".
4. On the next screen, find the JAR files (should be in C:\Program Files\Microsoft SQL Server 2000 Driver for JDBC\lib\), and add them under the "Classpath" tab. It will now be available under "Libraries" for future projects.
5. You can now create a connection to a specific database under the "Services" tab (next to "Projects" and "Files" in the top left of the screen). Select "Microsoft Sql Server 2000 (Microsoft Driver)" and format the "Database URL" like this:

jdbc:microsoft:sqlserver//YOURSERVER:1433;DatabaseName=YOURDATABASE

1433 is the default port, though your DBA may have changed it.

I posted a simpler version of this on the NetBeans.org FAQ page - they had the following title with no content on the answer page:

"Cannot Select Tables From MsSql 2000 Because It Says No Primary Key Next To Each Table But The Tables DO Have A Primary Key. What Do I Do?"

Looks like somebody was really frustrated, and posted a question in the hopes that somebody would answer it :-) Of course I was really frustrated seeing a question with no answer, so I added one after I figured it out myself.

Monday, May 12, 2008

.ashx files don't like blank output

Interesting - in the case of blank output, a regular .NET web page (.aspx) just outputs nothing to the screen, but a generic handler (.ashx) throws this:

"XML Parsing Error: no element found"

The generic handler I was working on was supposed to evaluate where the request was coming from, then do some processing and redirect. This worked fine until I uploaded it live. Turned out my code to evaluate the referrer was not looking for the correct live path. An easy fix, except that the misleading error lead me astray and had me checking .ashx permissions and Win 2k3 settings.

I finally converted the page back to a .aspx page, and the code returned a blank page, so I was able to quickly find the error after that. Once I corrected the path on the .ashx page, everything worked fine.

BTW, .ashx files have less overhead than regular .aspx pages, since they're not set up to serve up full web pages, so they're great processing-only pages, and non-html output like XML. More info here:
http://www.aspcode.net/Creating-an-ASHX-handler-in-ASPNET.aspx