Endjin.Licensing - Part 2: Defining the desired behaviour
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.
Sign up to Azure Weekly to receive Azure related news and articles direct to your inbox every Monday, or follow @azureweekly on Twitter.