I had a need today to quickly test some functionality of an existing assembly. It is a part of a web app, but can be tested without a web context. It also could be (and partly is) tested using unit testing (nunit in this case). Unfortunately, I need to break some rules and manually modify the app’s environment during the test, to see how it reacts.
We (at my work) are using a relatively unpopular, yet very nice library for authorization management: Microsoft Authorization Manager, also called AzMan. Interestingly, it is a COM library with official Interop wrapper available, for which we have created yet another simple wrapper, to better interface with our system.
AzMan lets us define roles and operations, and then mix and match them to assign appropriate user privileges to our users. Its API is very simple, but it offers very flexible and elegant model. Obviously, we define some roles, such as salespeople and administrators, and assign users to them. What is particularly convenient for us is that we DO NOT hard-code any roles directly in our source:
if User.IsInRole("admin") or User.IsInRole("sales") then enableFeatureX
Instead, our developers only need to care about actual privileges:
if User.HasAccess(FeatureX) then enableFeatureX
This gives us great flexibility regarding actual roles. One day we can give some privilege to engineers, another day to salespeople, and even to a single user, if he is the boss’s son. I was surprised (ok, not really) to see how often business rules need to be changed in a company. Especially when we deploy our application at several locations, all with their little quirks and differences. The idea of abstract “scopes” in AzMan elevates us to a whole new level, where we can customize role definitions to be partly inherited from “global” roles, but adjusted to local needs. And all this without really recompiling the app.
For example, in a disk management system scopes can be folders or disks, in a company they can define divisions, or – like in our case – separate manufacturing sites. AzMan also allows us to use simple code inside a privilege check:
if cost>1000 then return false
Such snippets of code are called BizRules and offer even more flexibility without recompiling the actual app, but we decided to stick to simple true/false flags instead.
In our application we keep a simple definition of roles and privileges, called security policy, in AzMan-specific XML file, though there are other ways. When our application is loaded, we initialize a global AzMan model and create a security context for each user. The problem I worked on today is: Is AzMan aware of any changes made to the xml policy file after it has been initialized? I have read different opinions, but our experience showed that our web app had to be restarted to reload the file. Interestingly, there is a method in AzMan called UpdateCache, which we didn’t use, but which seemed to fit our needs.
Not wanting to play with recompilation of our actual complex app, I firstly used PowerShell to slip into the assembly and play around, dynamically calling some code.
Of course the ISE is not necessary to get started with PowerShell, as we can use a simple PowerShell console, but the tool is a nice step up. Oh, and it includes live console, anyway.
The first thing we need to do is to import the assembly:
Then we need to instantiate the Model which in turn is used to create user context:
$model = New-Object Security.Model; $model.AppLoaded; # the above returns false $model.LoadApp("msxml://C:\Policy.xml", "App"); $model.AppLoaded; # now returns true $user = $model.LoadCtx("S-1-XXX"); $user.HasAccess([Security.Operations]::AccessDenied, ""); # returns false, and always should, for everybody.
Now the nunit-unfriendly part: I quickly update the Policy.xml file (using azman.msc in Windows 7) and give the user with ID “S-1-XXX” access to operation “AccessDenied”.
Since my PowerShell session is still live, I can test $user.HasAccess again.
# now we can update the underlying policy file # and run the commands below: $user.HasAccess([Security.Operations]::AccessDenied, ""); # still returns false because policy has not been reloaded
Turns out, the change was not picked up by our little Security assembly. Luckily, all we need to do is to add a call to UpdateCache:
$model.UpdateCache(); $user.HasAccess([Security.Operations]::AccessDenied, ""); # returns true! success!
This way I have successfully tested my code. I only need to unassign the AccessDenied operation, since my real unit tests require it to be never used by anybody.
In addition, I’ve accomplished two more things:
- learned PowerShell a tiny bit more
- created script that could be automated for integration testing, if only it had some real value ;-)
All the script commands above can be called either from PowerShell console, one be one, or pasted into .ps1 file (PowerShell script). Of course, the .ps1 file will not wait for you to fiddle with the policy.xml file, so you would have to add the appropriate code yourself (either call to pause/sleep or some kind of xml poke for full automation).
If you liked AzMan, you should take a look at NetSqlAzMan – open source implementation, without COM dependency but with very similar look&feel. I am considering upgrading to it from AzMan in the future.