How to: Grouping groups

GroupPolicy

Today I would like to show a tip for grouping active directory groups using LINQ to objects.

Suppose this scenario: You wanna retrieve all the roles a user belongs, grouping them by their domain name, as shows:

Groups under:
– Group name: All
– Group name: LOCAL
Groups under: BUILTIN
– Group name: Users
– Group name: Administrators
Groups under: PRIMARY_DOMAIN_NAME
– Group name: xxx1
– Group name: xxx2
– Group name: xxx3
– Group name: xxx4
– Group name: xxxN
Groups under: NT AUTHORITY
– Group name: INTERACTIVE
– Group name: Authentified users

(*) As you can imagine, some names have been deleted for security reasons :-)

We can accomplish this using only one LINQ to objects sentence, and a couple of extension methods (extending the NTAccount class):

public static IOrderedEnumerable<IGrouping <string, NTAccount>>
    GetGroupsUnderDomains(this WindowsIdentity identity)
{
    var groups =
        from grIdentity in identity.Groups
        where grIdentity.IsValidTargetType(typeof(NTAccount))
        select grIdentity.Translate(typeof(NTAccount)) as NTAccount into ntAccounts
        let domainName = ntAccounts.GetDomainName()
        orderby domainName
        group ntAccounts by domainName
            into domainGroups
            orderby domainGroups.Key
            select domainGroups;
    return groups;
}

public static string GetDomainName(this NTAccount account)
{
    string[] split = account.Value.Split('\\');
    return split.Length == 1 ? string.Empty : split[0];
}

public static string GetAccountName(this NTAccount account)
{
    string[] split = account.Value.Split('\\');
    return split[split.Length - 1];
}

Awesome! LINQ rules :-)

If you would try this code, it’s quite simple:

var groups = WindowsIdentity.GetCurrent().GetGroupsUnderDomains();
foreach (var dg in groups)
{
    Console.WriteLine(string.Format("Groups under: {0}", dg.Key));
    foreach (var g in dg)
    {
        Console.WriteLine(string.Format("  - Group name: {0}", g.GetAccountName()));
    }
}

HYEI, happy coding!

December 2010

How to: Check if current user is member of ‘domain admins’

The scenario

Sometimes in business applications it’s interesting checking if current user is member of the ‘domain administrators’ role. For example I am used to checking if current user has administrative privileges in order to showing some advanced configuration options (changing the application’s connection string, or allow creating new users).

privileges_thumb

IsInRole

To acomplish this we could use WindowsPrincipal class and its IsInRole method (this method checks if an user is member of a Windows role and returns a bool value). One of its overrides allows to pass the SID of the role or a constant value based on the enumeration WindowsBuiltInrole.

Note: For performance reasons, it’s recommended to use the override: IsinRole(SecurityIdentifier).

To check if current user is a local administrator we only need to do this:

WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
return wp.IsInRole(WindowsBuiltInRole.Administrator);

Notice that it’s realy easy, but as -its name indicates- WindowsBuiltInrole enumeration only contains local roles. So, if we would check if our user is member of a domain group, we should find the role SID, and then copy this value in our code.

As you can imagine, this is not the best solution, isn’t it? Well, let’s investigate a little bit more…

WellKnownSidType

Let’s take a look at the following enumeration WellKnownSidType, this enumeration provides commonly used security identifiers. Uhm… sounds good! Let’s try to use it in our code:

WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.AccountDomainAdminsSid, null);
return wp.IsInRole(sid);

Do’h!It seems that we need to pass the second argument called DomainSId…

CurrentUserIsDomainAdminError

DomainSid

What the hell means DomainSId? At MSDN we can read a brief explanation: Represents the domain SID, and this value is required for some WellKnownSidType values.

At this point our goal should be know the domain SID, but wait… how can I retrieve the domain SID? After spend some time surfing the Internet, the only solution I could find was an utility called PSGetSid from Mark Russinovich inside PSTools components. If you want try it, you can download this utility and execute it from your console to know the SID of your domain (type your domain name as “microsoft.com” o “net.volvo.com”):

DomainSidConsole_thumb

However, I’m pretty sure exists a better solution to this issue. Thus, let’s try to ask to our domain their SID, using the namespace System.DirectoryServices:

Domain d = Domain.GetDomain(new DirectoryContext(DirectoryContextType.Domain, getDomainName()));
using (DirectoryEntry de = d.GetDirectoryEntry())
{
    byte[] domSid = (byte[])de.Properties["objectSid"].Value;
    string sdomainSid = sIDtoString(domSid);
    Console.WriteLine(sdomainSid);
}

Here we need a couple of auxiliar methods. The first one retrieves the domain name, and the second one retrieves a string that represents a SID value (from a byte array).

public static string getDomainName()
{
    return IPGlobalProperties.GetIPGlobalProperties().DomainName;
}

public static string sIDtoString(byte[] sidBinary)
{
    SecurityIdentifier sid = new SecurityIdentifier(sidBinary, 0);
    return sid.ToString();
}

Then, the value of sdomainSid corresponds to the SID of our domain. Great! Now, we can use it to retrieve the SID of the domain administrators role.

Putting it all together

Like my collegue and friend @alegrebandolero, I’m also a fan of extension methods. So, let’s create an extension method for the WindowsIdentity class:

using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Net.NetworkInformation;
using System.Security.Principal;

namespace TestAD
{
    public static class SecurityExtensions
    {
        public static bool IsDomainAdmin(this WindowsIdentity identity)
        {
            Domain d = Domain.GetDomain(new
                DirectoryContext(DirectoryContextType.Domain, getDomainName()));
            using (DirectoryEntry de = d.GetDirectoryEntry())
            {
                byte[] domainSIdArray = (byte[])de.Properties["objectSid"].Value;
                SecurityIdentifier domainSId = new SecurityIdentifier(domainSIdArray, 0);
                SecurityIdentifier domainAdminsSId = new SecurityIdentifier(
                WellKnownSidType.AccountDomainAdminsSid, domainSId);
                WindowsPrincipal wp = new WindowsPrincipal(identity);
                return wp.IsInRole(domainAdminsSId);
            }
        }

        private static string getDomainName()
        {
            return IPGlobalProperties.GetIPGlobalProperties().DomainName;
        }
    }
}

That’s all. Now, use it as follows:

if (WindowsIdentity.GetCurrent().IsDomainAdmin())
{
    //Some actions…
}

Edit 12/14/2010: Since Windows Vista, each Windows user have a couple of security tokens. The first one is the normal token with limited privileges, and the second one only works when you ‘run as administrator’. This code only works if you are using the second token, running the application as administrator.

HYEI, happy coding!

December 2010

How to: Retrieve all controls in a form, using generics (II) – Linq at rescue!

Hi again,

In my previous post, I created a recursive function to retrieve all controls inside a form and its containers. Today, my colleague and friend Eduard Tomàs (software architect @ RAONA) has posted another solution to the same topic based on Linq. It’s quite simply and really, really pretty:

tshirt

A new approach:

Before starting, we need to solve this: Linq works over IEnumerable<T>, but Control.Controls property returns a ControlCollection type. In fact, nowadays, since we have Generics this class has no sense (like other 1.000 similar classes), but remember that Generics doesn’t appeared until .NET Framework 2.0. So, our first step will be retrieve an IEnumerable<Control> from a ControlCollection. With using an extensor method this will be really easy:

public static IEnumerable<Control> AsEnumerable
    (this Control.ControlCollection @this)
{
    foreach (var control in @this)
        yield return (Control)control;
}

Note that using this code we are able to transform CollectionControl to IEnumerable<Control>, and get full access to the power of Linq. Now, let’s create a method to retrieve all controls of a type, as follows:

public static IEnumerable<T>
    GetAllControls<T>(this Control @this) where T : Control
{
    return @this.Controls.AsEnumerable().Where(x => x.GetType() == typeof(T)).
    Select(y=>(T)y).
        Union(@this.Controls.AsEnumerable().
        SelectMany(x => GetAllControls<T>(x)).
        Select(y=>(T)y));
}

It looks cool, isn’t it? No loops, no ifs… only pure Linq power! :-)

There’s a small difference with my original solution and this new one. In my original solution I used an internal List<T> to copy all controls references. The new one only iterates over the original collection. There’s no internal lists. This is the power of Linq.

Another difference present in Linq solution is: We are returning an IEnumerable, so, we loose the ForEach method (because IEnumerable doesn’t implements this method). But building our own ForEach method is trivial:

public static void ForEach<T>
    (this IEnumerable<T> @this, Action<T> action)
{
    foreach (T t in @this)
    {
        action(t);
    }
}

HYEI, happy coding!

November 2010

How to: Retrieve all controls in a form, using generics (I)

Note: This is a very common question in MSDN forums. For this reason I decided to write this post, and use it for reference in future questions.

tshirt

The typical question: How to clear the content of all the textboxes in a form?

Answer: Using generics it’s really easy… First of all, let’s create an extensor method, that will returns a collection of all the controls of a type in a Form (or container). And after, we will use this collection to do an action over each returned item.

The extensor method:

public static List<T> GetControls<T>(this Control container) where T : Control
{
    List<T> controls = new List<T>();
    foreach (Control c in container.Controls)
    {
        if (c is T)
            controls.Add((T)c);
        controls.AddRange(GetControls<T>(c));
    }
    return controls;
}

This method retrieves the collection of Controls of a control and then, it call itself recursively, retrieving the content of all his containers.

How to use it:

this.GetControls<TextBox>().ForEach(p => p.Text = string.Empty);

In this code, we will use the extensor method directly in a form (because Form class inherits from ContainerControl, that inherits from ScrollableControl, and ScrollableControl from Control). Or, in other words, our Form will implement our extensor method. So, at compile time, we should specify the type of the controls we want to retrieve (in the example we use TextBox, but you can use Button type instead), and then, use ForEach to apply an action on each one of the items returned.

Moreover, if we want to retrieve only the controls into a container (GroupBox, Panel, TabControl or another), we should call the method for this particular control.

this.GroupBox1.GetControls<TextBox>().ForEach(p => p.Text = "hello");
this.Panel1.GetControls<TextBox>().ForEach(p => p.Text = string.Empty);

Applying several actions on each control:

In most cases, we would apply several actions to each control (not only one). In this case, it’s quite easy: The only thing we have to do is create a method that receives a parameter of this type, and call it passing the control as an argument:

this.GetControls<TextBox>().ForEach(p => ApplyFormat(p));

private void ApplyFormat(TextBox text)
{
    text.BackColor = Color.LightGray;
    text.ForeColor = Color.Red;
    text.Text = "hello";
    text.TextAlign = HorizontalAlignment.Center;
}

Or maybe, using an ‘action delegate’, both options are correct:

this.GetControls<TextBox>().ForEach(p =>
{
    p.BackColor = Color.LightGray;
    p.ForeColor = Color.Red;
    p.Text = "hello";
    p.TextAlign = HorizontalAlignment.Center;
});

Happy coding! :-)

November 2010