Mastodon

Creating your own custom Subversion management layer

I’m a big fan of Subversion. As far as source control management products go I find it pretty intuitive and very easy for people to get to grips with regardless of their degree of technical competence. The autonomy it has from the prime time technology stacks gives it broad appeal and the fact that it’s freely available is surely a significant part of why it’s so popular today. If you’re looking for pure source control management, it’s hard to go past SVN.

Following is the process I recently went through to provision a Subversion instance with a custom management layer to surface a few key features through a web UI. I’m not going to go into great detail code wise, rather offer some info on the nuts and bolts required to couple the system together with a few key code snippets as required. Hopefully this will give someone else with a similar need a jumpstart in the future.

The goal

Recently I set out to source a new Subversion instance to support a broad range of people working across multiple, autonomous projects in different locations around the world. What I really wanted to do was find a Subversion distribution which would allow me to break rights down into three tiers:

  1. End users; normal Subversion users who can be granted read and write permissions
  2. Repo managers; named individuals who have the right to create a new repo on demand plus manage permissions in existing repos they have rights to
  3. System administrators; all of the above across all repos

All the roles would need to be able to perform their duties via a web based UI and it had to run in a Windows environment leveraging Active Directory accounts for authorisation purposes. Oh, and cost is important. If I could achieve this model then the administrative burden would be moved back to the people actively creating and managing projects rather than those supporting the environment.

Now before anyone jumps up and starts singing the praises of distributed source control management systems such as Mercurial and Git, these were considered but some key constraints ruled them out. Due to various circumstances, namely product familiarity with a large audience and a desire to make the switch “low friction”, these products were off the cards for the moment.

Currently available options

There are two Subversion paths you can go down:
  1. Run Subversion directly by installing your own instance of SVN and Apache (very good overview of that here)
  2. Run a packaged distribution of Subversion which automates the install and provides a GUI and other easy useful management features you don’t find in the previous option

The first option is out right away as there’s no GUI. The second option is closer but out of all the packages I looked at, nothing can distribute management the way I needed them to. When it comes to creating repos and managing access rights, nothing out there seems to do the job at the moment.

If you want something done properly…

As the saying goes, sometimes you just need to do it yourself. We’re not talking about reinventing the wheel here; Subversion is a perfectly good tool in terms of managing source code, it’s really just the configuration management part I’m talking about. What I was envisaging was a web layer that would facilitate creating the repo and managing the permissions. That’s it. In my mind, the only hurdle I needed to tackle was finding a suitable API for .NET to chat to and it would be a piece of cake.

Choosing a Subversion distribution

This was actually a pretty easy choice as there’s one Subversion distribution which stands head and shoulders above the rest in terms of ease of use, price and functionality; VisualSVN Server. This is a very small footprint product (4Mb installer), gets up and running in a couple of clicks and just works every time. I’ve run it directly on a number of PCs for version control on personal projects and never had the slightest glitch with it.

VisualSVN

The other thing VisualSVN Server gives you is a nice MMC style UI to perform common management tasks such as creating repos and managing permissions. It also facilitates server configuration activities such as the port, authentication method and whether an SSL certificate is used.

A final note on product variants; the Standard edition does pretty much everything anyone would need but there is an Enterprise edition. This adds logging functionality for operational tasks such as a user checking out code and allows for remote administration. Nice features, but not worth US$500 based on my particular requirements so I didn’t bother purchasing the license after the trial expired (hence the notice in the screen grab above).

Talking to Subversion

There are a couple of different APIs out there but the most common and most well received seems to be SharpSVN. I also later discovered that the guy behind the project, Bert Huijben, is pretty active on Stack Overflow so there’s a familiar channel available to get support on the product.

SharpSVN is essentially a wrapper for the native SVN commands and as such, it can only perform functions these commands natively expose. This is fine for activities such as creating repositories or reading log messages but the one thing it can’t do that I really needed is to manage permissions. Here’s how Bert explained it:

SharpSvn is mostly a wrapper around the Subversion api and the Subversion
api doesn't implement an api for managing subversion permissions.
and
Different configurations of Subversion have different repository permission
settings.
Right, so the only option was to look at how VisualSVN implements permissions then devise a mechanism to edit these directly.

Creating repositories

The first thing to understand with creating repositories is that the underlying command is svnadmin create, a command which needs to be run on the server. Many of the commands the SharpSVN API exposes can be executed remotely (such as reading log messages) but creating repos is not one of them.

Here’s how the new repo is created:

using (var svnRepoClient = new SvnRepositoryClient())
{
  svnRepoClient.LoadConfiguration(repoPath);
  svnRepoClient.CreateRepository(repoPath);
}

Firstly, the repoPath parameter needs to be physical path of where the repo will be created so in most cases is will be something like c:\Repositories\MyRepo. Next a bit of background on the LoadConfiguration statement:

SvnClient.LoadConfig​urationDefault() (just like svn.exe) assumes the users registry hive is always available (e.g. it can read from HKEY_CURRENT_USER). It uses a few windows api calls on retrieving which directory to use for the settings.

Using .LoadConfiguration(​<path>) skips these procedures as it says: Only load these settings. (+- equivalent to using --config-dir <path> on svn.exe)

In short, it’s a performance thing. As a sidenote, later on I added some functionality to read log messages from a revision range and without loading the configuration first, the process was extremely slow (actually exceeding the default ASP.NET timeout).

Managing repo permissions

As mentioned earlier, the structure of the permissions file is largely dependent on the Subversion distribution. VisualSVN Server takes the approach of using a file named authz-windows in the root of the repositories folder for any Active Directory based authorisations. This file then describes, for both the server as a whole and each repo, who has access and whether it’s read only or read and write. Here’s what it ends up looking like:

[/]
S-1-5-21-8439203732-8503583263-2345657657-123442=rw

[MyRepo:/]
S-1-5-21-2345435435-2346665755-2343245653-99804=rw

This file is granting one account rights over the entire repository and another account rights to just “MyRepo”. You’ll notice each account is referenced as a Windows SID rather than a username but you can resolve one to the other pretty easily if required:

var account = new NTAccount(username);
var sid = (SecurityIdentifier)account.Translate(typeof(SecurityIdentifier));

Creating a data layer

Something that became clear pretty early on was that there was information I wanted to capture which wasn’t natively stored anywhere within VisualSVN. For example, I wanted to collect a little bit more information about the project that was going into the repo as well as some meta data about who created it and when. I also had plans to introduce more functionality later on that would require fast access to both a list of repos and permissions and I wasn’t real keen on the idea of trying to derive these from the file system. The final straw was that managing the permissions file was going to be a bit nasty if the only source of permissions data was SIDs in a text file and this also didn’t provide a mechanism for modelling admin rights or repo creator rights.

So the data layer is actually pretty simple. All it really needs is a Repo table, a Role table and a mapping table including the identity of the user who has been granted permissions. Obviously this can be expanded a little to capture more info but there are really only two entities in the system plus the mapping table.

Securing the application layer

Now that we already have a data model to reflect the permissions we want to apply it’s pretty simple to drop a security-conscious web app on top. I just created a simple role provider implementing the GetRolesForUser method then leveraged security trimming to protect the interfaces to authorised users.

Putting it all together

Firstly the repo management page; all that’s required here is to provide a UI which allows authorised users to create new repos. Just a single textbox will suffice although in my case I ended up asking for a little more information. The role provider secures the page and the SharpSVN API creates the repo followed by a quick LINQ to SQL call to create an entry in the data layer.

Secondly the permissions management page; I redirected straight into this after creating a repo as normally the first thing you want to do is to provide access to other people. This page only needs to provide a means of selecting an identity (I had an existing source of identity data) then choosing whether to give the user admin, reader or writer permissions. After each change I’m just rewriting the Subversion authorisation file in its entirety. The only thing you need to be cautious of here is that if you then use the VisualSVN Server UI to change permissions they’ll be overwritten the next time the permissions file is regenerated from the data layer.

Making it live

Something that only struck me once I got out of development mode and onto a live server was that I was trying to expose both Apache and IIS on the one machine and that I wasn’t going to be able to run them both over port 80. My original intention had been to bind one domain name to Subversion and another to the management interface. There are a number of ways around this including writing ISAPI filters for IIS, using a reverse proxy or most elegantly, running multiple IP addresses (one for each domain) on separate NICs and binding them to the appropriate server.

All the above options were either getting clunky or would require more investment in time and probably dollars so I took the easy route and broke out the duct tape. I left port 80 bound to VisualSVN then edited the default file Apache serves up for any requests to the root (located at C:\Program Files\VisualSVN Server\htdocs\index.html) and added a JavaScript redirect to the same URL but on port 81 which I then bound to the IIS site with the management interface. To be honest, I felt a little dirty after doing it and may still pursue the multiple IP addresses option but this got things up and running quickly with the only real downside being a port appearing after navigating to the site.

Conclusion

All said and done this was a pretty simple little piece of work which got me back into some code for a while and threw a few curve balls. The underlying Subversion structure hasn’t been touched so VisualSVN upgrades shouldn’t be a problem in the future and I can still pick any repo and ship it somewhere else.

Most importantly though, the users are happy as they’re now empowered to manage their own repos without compromising on the security of others. The only problem I have now is resisting the temptation to use the platform to add more features!

Things I omitted in this post

For the sake of simplicity I’ve left out information about some of the frills I added along the way. This includes granting permissions to AD groups or to everyone, email notifications when repos are created, account the VisualSVN service runs under and some other features I added to surface repo data to the web. I also haven’t touched on pre-commit hooks; I’m saving this one for a subsequent post as it’s a project in and of itself.

.NET Subversion
Tweet Post Update Email RSS

Hi, I'm Troy Hunt, I write this blog, create courses for Pluralsight and am a Microsoft Regional Director and MVP who travels the world speaking at events and training technology professionals