Returning to CCTray
In my last post (read it here) I had started looking at modifying CCTray to authenticate. I had added an interface to handle authentication (IAuthenticationMode), modified the UI and even managed to persist the settings. However, I ran into a problem on doing the actual authentication. Basically, there was no unified place that had everything I needed
Now it’s time to return to CCTray and refactor it until I can authenticate.
My Starting Point
Since I modified ICruiseManager in the remote library to include the authentication methods, my starting point will be ICruiseServerManager. Last time I had modified it to include the Login() and Logout() method, but I had assumed the parameters would come from an external source. Now I’m going to modify it again. I’ve removed the ServerUrl and Transport properties, instead I’ve replaced both of these with a Configuration property – this will return a BuildServer instance. I also added a new property called SessionToken, and modified the Login() and Logout() method to not have any parameters (since the values will come from Configuration).
Attempting to build breaks RemotingCruiseServerManager, HttpCruiseServerManager and CachingCruiseServerManager. CachingCruiseServerManager was the easiest to fix – I just implemented the changes and handed them over to the wrapped manager. The other two were a bit harder!
Looking at RemotingCruiseServerManager I saw that it already took in a BuildServer instance to its constructor – so that made things easier. I just stored this instance, and then modified the properties as required. SessionToken I just added as a string and then added the property getter. Logout() was also easily modified – I just removed the input parameter and instead used the local session token.
Login() required a bit more work. The configuration doesn’t actually hold an instance of IAuthenticationMode, so I needed to generate one. Now, for my transport extension changes I had added a class called ExtensionTransportHelpers that included a static method for instantiating an object from its type name. So I modified this class to also include a method for instantiating an IAuthenticationMode instance. This only required a little bit of refactoring – moved the common code into its own method and then called this from the two retrieve methods. I also renamed the class to ExtensionHelpers to show its more general nature now.
Now, all I needed to do was instantiate the authentication class, generate the credentials (via GenerateCredentials()) and pass these onto the Login() method via remoting. The final step was to check the results and return true if the session was generated. Turned out to be nice and simple!
Finally, HttpCruiseServerManager. This was similar to RemotingCruiseServerManage, but instead of actually implementing the Login() and Logout() methods I just threw a NotImplementedException.
With these classes now modified, it was a simple matter to fix the other errors where it was depending on the old properties to retrieve the values via the configuration. I also updated the unit tests so they built correctly, but I didn’t test them. I’ll come back to them later on once I’m happy this approach works (I know, not proper TDD, but this is more of a try-and-see approach, rather than properly designed.) However, CCTray still runs so I didn’t break anything major
Let’s Authenticate
Last time I tried to authenticate I ran into problems – this time I’m ready to handle it. Which brings me to MainFormController. CCTray is obviously built using a MVP/MVC approach, with MainFormController being the controller for MainForm. There are a couple of methods in here to stop and start the server monitoring – StartServerMonitoring() and StopServerMonitoring(). These start an AggregatingServerMonitor instance polling, but as AggregatingServerMonitor doesn’t have any start or stop methods I can’t use it. Back to MainFormController - the AggregatingServerMonitor instance is generated from an array of ISingleServerMonitor, which is stored in MainFormController. So I’m going to use these to login/logout instead.
The default instance of ServerMonitor stores an instance of ICruiseServerManager, so I now have access to my login/logout methods. To call these, I added Start() and Stop() methods to ServerMonitor(plus its main interface – ISingleServerMonitor). This merely calls the Login() or Logout() method on ICruiseServerManager. I also needed to modify SynchronizedServerMonitor since that also implements ISingleServerMonitor, but that is just a wrapper around an instance of another ISingleServerMonitor.
Nearly there – the final step is to modify the StartServerMonitoring() and StopServerMonitoring() methods on MainFormController. These methods just iterate through each ServerMonitor and calls either Start() or Stop(). All done
Let’s run it!
Oops, I forgot a scenario – Peter didn’t want any security! Since my current version has no security it crashed with an error
No worries, I just modified the Login() and Logout() methods to bypass the remoting calls when there is no security. Now it works – Peter is happy again.
Some Authentication Modes
In my last post I promised that I’d build some authentication modes – well, now I’m finally at that point. This will also be “THE TEST” to see if all my changes work together!
So, my first authentication mode will match the UserNameAuthentication mode on the server. I’m going to call this class UserNameAuthenticationMode (wow!) I’ve added this to its own folder/namespace in CCTrayLib and implemented IAuthenticationMode on it. Settings is just stored as a string – since there’s only going to be one setting I’ll leave it at that. Validate() returns true if the Settings is not null or empty.
Configure() will open up a new window to prompt the user for their name – this then gets stored into Settings. The dialog looks like this:
GenerateCredentials() is a bit harder – I don’t have an implementation of ISecurityCredentials on the client, so for the moment I’ve just copied over UserNameCredentials from the server code.
After this, I then had to return to ConfigureServer to finish off some implementation details (like the Click events for the buttons, loading the list of types and initialising the authentication modes).
Some Testing
I’ve now completed the changes, built an authentication mode, so it’s time to do a test.
- Run the code, good so far…
- Configure the server, everything looks good…
- Save the changes, oh no, there’s an error!
Turns out UserNameCredentials isn’t marked as serializable. So, I applied this attribute and tried again.
This time the server threw an error. It couldn’t deserialise the instance. Ok, this time I moved the whole class into Remote and tried again.
Now it works!
I’ve managed to get authentication working from end-to-end using .Net remoting and CCTray.
Next Steps
This was a bit more of a challenge than I thought, but I managed to get there in the end. Now it’s time to touch the final component in CruiseControl.Net – the dashboard! I’ve been avoiding this for a while now, but it’s time to take the plunge.
In my next post I’ll start looking at the dashboard. Now I’ve never looked at the dashboard code before, so I have no idea how it works. Therefore, I’m going to spend a bit of time looking into the code and trying to figure it out. Once I know how the dashboard works I’ll start to add some code for authentication. Now this might take a while, so if I don’t post for a few days you’ll know why. But don’t worry, I am going to finish this project
RSS - Posts