We've open sourced a lightweight licensing framework we've been using internally over the last couple of years. In a 5 part series I'm covering the motivation behind building it, how you can use it, how you can extend it for your own scenarios and how you could use it in a real world situation:
- Part 1: Why build another licensing system?
- Part 2: Defining the desired behaviour
- Part 3: How to create and validate a license
- Part 4: How to implement custom validation logic
- Part 5: Real world usage patterns
I've been an advocate of BDD for a number of years, and have tried most of the frameworks, but in the last 18 months have established a monogamous relationship with Specflow. I find that writing higher level specifications, in English, really helps me focus on the behaviour of the system I'm creating but doesn't suffer from the brittleness & fragility that traditional unit test suffer from, when it comes to refactoring the underlying code. I started by writing a specification that covered the main scenario I wanted to support in Vellum:
Feature: Create a License In order to run vellum As a user I want to be able to obtain valid licenses for my runtime requirements Scenario: Create a Vellum License for the Free Azure Website rate Given I want a license for my Azure Website http://mysite.azurewebsites.net And I have a staging slot configured at http://mysite-staging.azurewebsites.net And I want to run on a free subscription And my billing subscription commenced today And I have entered my Azure subscription details When I request my license Then it should be a valid license And it should have been issued today And it should expire on the last day of this month
At this point I had the epiphany that what I needed to build was not a complete licensing platform (like Rhino.Licensing), but a framework that would allow me to easily create custom validation rules; notions like a website address or product SKU are not the concerns of the licensing framework, they are concerns of the product consuming the licensing framework – in this case Vellum.
So, I deleted the specification and wrote a second draft, which covered the more intrinsic requirements of a licensing framework:
Feature: Validate License In order to allow users to only use my software with a valid license As a software provider I want to be able to validate the license based on its criteria Scenario: Validate a 'Subscription' License that was issued today Given they want a 'Subscription' license And their license was issued today And I generate their license When they validate their license Then it should be a valid license And it should have been issued today And it should expire on the last day of this month
The intrinsic data the license should contain is a license Id, so that we can make the license uniquely identifiable, a date of issue, a date of expiration (when the license should invalidate), a license type (subscription, annual, perpetual) and a collection of user definable key / value pairs to act as an extensibility point for users to build their own validation logic on.
Once the initial success path was apparent, a failing scenario then became immediately obvious:
Scenario: Validate a 'Subscription' License that expired last month Given they want a 'Subscription' license And their license was issued a month ago And I generate their license When they validate their license Then it should be an expired license And it should have been issued last month And the license should have expired on the last day of last month
The next desired behaviour for a licensing system is to detect that a license may have been tampered with (for example manually modifying the expiration date):
Scenario: Tamper with the expiration date of a license Given the user is issued with a valid 'Subscription' license And the user tampers with the expiration date of the license When they validate their license Then it should be an invalid license
Once the specifications were fleshed out it didn't take long to create the initial implementation of the framework in order for the specifications to execute successfully. Once that was done, it was then possible to create a new project to contain the Vellum specific validation rules. Having had a few more days to mull over the licensing behaviour we wanted to implement for Vellum, it was quite simple to recreate and elaborate my very first specification:
Scenario: Free Azure Hosting Plan License that was issued today and is running on a Free Azure Hosting Plan License with a valid host name is valid Given they want a 'Subscription' license And their customer id is 'c8ca3109-6fbf-4b75-b6ee-67a1a0a56bd1' And their subscription id is '03c1b02b-7806-4e94-8044-140d95ff52d1' And their license was issued today And their SKU is 'Free' And their development host is 'localhost' And their site name is 'mysite' And they are running the site on a 'Free' Azure Hosting Plan And they are hosting on a website called 'mysite' And I generate their license When they validate their license Then it should be a valid license And it should have been issued today And it should expire on the last day of this month And the metadata should have a value of 'localhost' for the development host And the metadata should have a value of 'mysite' for the site name And the metadata should have a value of 'Free' for the SKU And the metadata should not have a value for instance size And the metadata should have a value of 'c8ca3109-6fbf-4b75-b6ee-67a1a0a56bd1' for the customer id And the metadata should have a value of '03c1b02b-7806-4e94-8044-140d95ff52d1' for the subscription id
Once this was completed, it was very straightforward to flesh out another 8 features containing a total of 47 different scenarios, covering off all of the different permutations we could think of.
In the next part, I'll take you through a step by step guide for creating and validating a license.