Adding a RabbitMQ Configuration File When Running as a Windows Service

A little context

I was having a hard time adding a rabbitmq.config file today. I added it in the right location, restarted the RabbitMQ Windows service and the logs still showed that the configuration file was not found.

If you’re experiencing the same problem, you’ll see a similar error message in the RabbitMQ log file after you restart the Windows service.

=INFO REPORT==== <date here>
node			: rabbit@<server>
home dir		: C:\Windows
config file(s)	: c:/path/to/config/rabbitmq.config (not found)
...some other stuff...

This will happen if you just install your RabbitMQ server with the normal, default installation process and then try to add a configuration file later. By default, RabbitMQ doesn’t install configuration file and just uses all it’s defined defaults.

Solution

I read the documentation a little more closely and finally came upon this line: “Windows service users will need to re-install the service after adding or removing a configuration file.”

Oops. Guess I should have read the documentation more closely the first time around! The easiest way to do this is as follows (start a command prompt as administrator):

> cd "C:\Program Files (x86)\RabbitMQ Server\rabbitmq_server-3.6.0\sbin"
> .\rabbitmq-service.bat remove
> .\rabbitmq-service.bat install
> .\rabbitmq-service.bat start

Of course, The path you cd into will depend on the version of RabbitMQ you have installed.

Working with x.509 Certificates in .NET

I found a lot of very helpful details about certificate storage and usage in Windows and .NET in this article.

Particularly, the explanation of where certificates and private keys can be stored for UserStore certificates was very helpful in debugging some issues I’m currently having on a production server.

Paul Stovell’s blog post

Permutations with Iterators in C#

I recently ran across a need to generate all permutations of an array of objects in some unit testing I was doing.

The specific situation was testing that, no matter what the order, when certain messages were picked up by an NServiceBus Saga, that a certain state was consistent after all messages were received. Since I explicitly wanted to test the state no matter what order the messages arrived in, I needed all permutations of those messages so I could send them to the Saga in each order and test the resulting state.

I found this super article on the subject that gave all the code I needed: Generating Permutations with C# Iterators.

Thanks IanG on Tap!

Active Directory Authentication in ASP.NET MVC 5 with Forms Authentication and Group-Based Authorization

I know that blog post title is sure a mouth-full, but it describes the whole problem I was trying to solve in a recent project.

The Project

Let me outline the project briefly. We were building a report dashboard-type site that will live inside the client’s network. The dashboard gives an overview of various, very important information that relates to how the company is performing on a hourly basis. So, the dashboard is only available to a certain group of directors.

To limit the solution to the these directors, authentication and authorization would go through their existing Active Directory setup by putting the authorized users in a special AD group.

The Problem

Getting authentication to work was a snap. Microsoft provides the System.Web.Security.ActiveDirectoryMembershipProvider class to use as your membership provider. Putting an [Authorize] attribute on my action methods or entire controllers was all I needed to get it working (besides, of course, the system.web/authentication web.config updates and a controller to show my login form and handle the submit credentials).

Here’s my relevant web.config setup:

<connectionStrings>
    <add name="ADConnectionString" connectionString="<ldap connection string here>" />
</connectionStrings><authentication mode="Forms">
    <forms name=".AuthCookie" loginUrl="~/login"/>
</authentication>
<membership defaultProvider="ADMembershipProvider">
    <providers>
            <clear/>
            <add name="ADMembershipProvider"
                 type="System.Web.Security.ActiveDirectoryMembershipProvider"
                 connectionStringName="ADConnectionString"
                 attributeMapUsername="sAMAccountName"/>
    </providers>
</membership>

The tough part came when I wanted to limit access to users in that AD group. Microsoft doesn’t provide a RoleProvider along with its ActiveDirectoryMembershipProvider. So, what to do?

I tried several methods I found online. Most of them were based on creating my own custom RoleProvider and querying AD to iterate through the user’s groups (treating them like roles) and seeing if one of them matched my AD group I was looking for. However, I could never get it to work. Each code example I found eventually gave me this AD error when I iterated through the current user’s AD groups:

The specified directory service attribute or value does not exist.

The Solution

Eventually, I found a solution online that worked. Instead of setting up a custom RoleProvider, all it involved was creating a custom AuthorizeAttribute for your MVC controllers (or action methods) that checked the user’s .IsMemberOf method to see if the member belonged the sought after group (or groups). I don’t know why this method does not cause the same AD error as describe above, but I’m glad it doesn’t! All I can assume is that it queries AD in a more friendly way.

Here is my custom AuthorizeAttribute:

public class AuthorizeADAttribute : AuthorizeAttribute
{
    private bool _authenticated;
    private bool _authorized;

    public string Groups { get; set; }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        base.HandleUnauthorizedRequest(filterContext);

        if (_authenticated && !_authorized)
        {
            filterContext.Result = new RedirectResult("/error/notauthorized");
        }
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        _authenticated = base.AuthorizeCore(httpContext);

        if (_authenticated)
        {
            if (string.IsNullOrEmpty(Groups))
            {
                _authorized = true;
                return _authorized;
            }

            var groups = Groups.Split(',');
            string username = httpContext.User.Identity.Name;

            try
            {
                _authorized = LDAPHelper.UserIsMemberOfGroups(username, groups);
                return _authorized;
            }
            catch (Exception ex)
            {
                this.Log().Error(() => "Error attempting to authorize user", ex);
                _authorized = false;
                return _authorized;
            }
        }

        _authorized = false;
        return _authorized;
    }
}

Notice that I also included a little code to distinguish between the user not being authenticated (which the call to base.AuthorizeCore takes care of) and not being authorized. Without the code in HandleUnauthorizedRequest, if the user successfully logs in but is not in the AD group, he just sees the log in screen again which doesn’t communicate the problem very well.

The this.Log() code uses a Nuget package called this.Log. The LDAPHelper class is something I wrote. The code is below:

public static class LDAPHelper
{
    public static string GetLDAPContainer()
    {
        Uri ldapUri;
        ParseLDAPConnectionString(out ldapUri);

        return HttpUtility.UrlDecode(ldapUri.PathAndQuery.TrimStart('/'));
    }

    public static string GetLDAPHost()
    {
        Uri ldapUri;
        ParseLDAPConnectionString(out ldapUri);

        return ldapUri.Host;
    }

    public static bool ParseLDAPConnectionString(out Uri ldapUri)
    {
        string connString = ConfigurationManager.ConnectionStrings["ADConnectionString"].ConnectionString;

        return Uri.TryCreate(connString, UriKind.Absolute, out ldapUri);
    }

    public static bool UserIsMemberOfGroups(string username, string[] groups)
    {
        /* Return true immediately if the authorization is not
        locked down to any particular AD group */
        if (groups == null || groups.Length == 0)
        {
            return true;
        }

        // Verify that the user is in the given AD group (if any)
        using (var context = BuildPrincipalContext())
        {
            var userPrincipal = UserPrincipal.FindByIdentity(context,
                IdentityType.SamAccountName,
                username);

            foreach (var group in groups)
            {
                if (userPrincipal.IsMemberOf(context, IdentityType.Name, group))
                {
                    return true;
                }
            }
        }

        return false;
    }

    public static PrincipalContext BuildPrincipalContext()
    {
        string container = LDAPHelper.GetLDAPContainer();
        return new PrincipalContext(ContextType.Domain, null, container);
    }
}

My code is mostly based on example code I found on a very helpful StackOverflow post.

To use this code, all you have to do is use your custom AuthorizeAttribute instead of the built-in one. Something like this:

[AuthorizeAD(Groups="Some AD group name")]
public class HomeController : Controller
{

}

Apply Git Patches to SVN Repository

Creating Subversion patches with Git