Previously
In my last post (here), I had added authentication and sessions to the web dashboard. While authentication is an important part of security, it is only half the story. The other half is authorisation. Previously I had modified the server components (Core, Service and Console) so they used a session token to identify a user and then work out whether they had permission to perform the action. So, to complete the web dashboard modifications I need to pass the session token to the force/about build method calls.
Where to Start
While there are a couple of possibilities, the place that I’m going to start is with IFarmService. This is the interface that is responsible for passing server requests along. Previously I had modified it so the ForceBuild() and AbortBuild() methods took in a session token parameter. Now I need to find the places where this is actually called and pass in the valid session token.
These methods are called in two places:
- ForceBuildXmlAction (ForceBuild() only)
- VelocityProjectGridAction
Now both of these places I have modified previously to accept a token from a CCTray client. Now I need to modify VelocityProjectGridAction to also look for a token in the query string (since ForceBuildXmlAction is an XML action I don’t need to modify it again).
To do this I already have an interface defined – ISessionRetriever. So first change is to modify the constructor to request an instance of ISessionRetriever and store it locally. Then I modified ForceBuildIfNecessary() to first check the request for a session token (previous change), then if not found to use the session retriever.
And that’s all there is to it. Now a user can login and force/abort a build (assuming they have permission).
Session State
Previously I had added session state by storing it in the query string. While this works for a single machine it doesn’t handle multiple remote servers. Additionally, while I’ve stored the session token in the query string, others might want to store it in a different place (e.g. cookies, file system, etc.) or even not at all (e.g. if using integrated security).
To get around this I’m going to modify the session state mechanism so it is configurable. Already I’ve made the login mechanism configurable, now I’m going to expand the configuration to add a session store. To do this I’ve added a new interface called ISessionStore. This has two methods: RetrieveStorer() and RetrieveRetriever(). This methods merely retrieve an instance of ISessionStorer and ISessionRetriever - it is up to the session store implementation to generate and return these.
Next step was to modify NetReflectorPluginConfiguration to allow an administrator to set an ISessionStore. I’ve added property called SessionStore, plus its backing field. This defaults to an instance of QuerySessionStore (my default implementation of ISessionStore). This has been decorated with a ReflectorProperty attribute, and since it is a singleton I’ve set InstanceTypeKey to be “type”. Now it can be automatically loaded.
I also modified IPluginConfiguration since this is the interface that is used. Finally I modified SetupObjectSourceForRequest() in CruiseObjectSourceInitializer to use the new SessionStore property in IPluginConfiguration. And now it is possible to change the session store mechanism – or at least that’s what I thought!
The problem is the order in which the configuration and url builders are loaded – the url builder needs to be loaded first, followed by the configuration. But the session storer is needed to load the url builder, and this needs the configuration to be loaded. I got around this problem by making the ISessionStorer a property on DefaultUrlBuilder (instead of a constructor argument) and manually setting after the configuration has been loaded.
And now it works
Security Completed
That now completes my first iteration of security. I have added security to all the components of CruiseControl.Net and in a way that is configurable and expandable. While it works, I know it still needs refining and that’ll be my next task.
However if you want to play with it, it is now ready and can be tested
When I have some more time in future I’ll start refining the security components so they are more user-friendly. I’ll also add some more implementations of different security components. And not to mention documentation!
RSS - Posts