Security within an embedded software scenario is a lot like an arranged marriage; neither party really knows much about the other and there’s no guarantee they’re compatible. If the foundation is good, it can work for the long-term. Or if the parties haven’t thought about the future, it can be the start of a lifetime of trouble.
If you are a developer of embeddable software, or are looking to follow best practices, you have a few options for how to architect security within your own product: you can choose a security model and enforce that upon your customers, you can commit to adding new models to your product as the need arises, or you can be abstract.
Of the three, imposing a model on your users has to be considered the weakest option. It has the benefit of allowing your development team to know exactly what to expect, but that’s about where the positives end. With this approach, you’re guaranteed to limit your prospective customers due to incompatibilities. You run the risk of being the guy who bought an HD-DVD player because there’s no way a blue laser reader can really be that good.
Committing to adding new models to your collection as the need arises is a noble endeavor, but doomed to failure. No matter the size of your shop, you can’t hope to keep up with the pace of innovation from the rest of the world. While this approach does ameliorate the problems of being heavily opinionated to a degree, it’s still not tenable.
Being abstract not only works for Jackson Pollock, it works incredibly well for embeddable software. This topic is discussed throughout the rest of this article, so I won’t drone on about it here. Though I will say if you give an abstraction to users for their custom solutions, you can push the burden of providing an implementation to them, and that’s pretty sweet in my book.
Given the above discussion of merits, for this article we’re going to choose the abstract route. I am of course referring to coding to abstractions when I say we’ll be abstract here. Coding to abstractions seems to be a thing that we as developers all learn in CS101, and then half of us forget that the concept even exists.
Coding to an abstraction in this sense means not targeting a single security model, but rather coding to an abstract security object. With this approach, you will need to provide an interface to bridge the security in your system, with the security from the system you’re embedded in. For example, consider the following interfaces:
public interface ISecurityProvider
{
// This could easily be int userID, or whatever types you wish to support. This allows you
// to check access on a case by case basis.
bool hasAccess(string userName, string asset);
// This options requires an IUserToken, which should be an implementation of the clients
// security model.
bool hasAccess(IUserToken user, string asset)
}
The IUserToken, mentioned above, allows your customer to import the security information from their system into yours. Whether they are using Active Directory, Oauth, or a custom solution, as long as they can write a simple implementation of this interface to retrieve their security data, you’ll be good to go.
public interface IUserToken
{
// Include any token related properties and functions here. We’ll just secure one item
Dictionary<string, bool> rights { get; }
}
We’ve now provided two ways of applying security for our clients. Let’s take a look at a sample implementation of these interfaces.
public class CustomSecurityProvider : ISecurityProvider
{
public bool hasAccess(string userName, string asset)
{
// Our security is very strict here.
return true;
}
public bool hasAccess(IUserToken user, string asset)
{
if (user.rights.ContainsKey(asset))
return user.rights[asset] == true
else
return false;
}
}
While these examples are somewhat contrived, they do demonstrate some important principles. Namely, with just these, we have the foundation for an extensible, albeit limited, security architecture. The only remaining piece is using this interface within your code.
To accomplish this, we will be declaring our ISecurityProvider as a parameter to a function which does some work. In your example, this will probably be more substantial than return Hello, Coder.
public string testSecurity(ISecurityProvider security)
{
var message = string.Empty;
if (security.hasAccess(“Mason”, “test”)
{
message = “Hello, Coder”;
}
return message;
}
Excellent! We now have in place a system which is extensible by our internal team as well as our customers. But why? Why take the time to think this style out? It does require some forethought and will add a little time to your bottom line. To that end, I have a couple of closing thoughts.
Thought the first: this article about abstracting security to an interface is a microcosm for development in general. This is but one small area; imagine using abstractions everywhere. Imagine a world where your IAwesomeWidget takes an ISecurityProvider. Now you’re just coding to ideas, things that will be conceived at a later date. You don’t care what IAwesomeWidget your customer has; you just care that he has one.
Can you see the power in that? The power to not have to make decisions about how these things are done; only caring that they are done.
Thought the second: I shall continue this thought the number thing for the rest of my days.
Thought the third: imagine your business prospects when you no longer have to ask “Are you using Active Directory for security?” Or “Do you use our AwesomeWidget?” Abstraction frees you from the burden of caring exactly what your customer has. You stop being “The official provider of Active Directory, AwesomeWidget shops” and start becoming “The official provider of Blah, regardless of your security or the awesomeness of your widgets.” I won’t speak for you, but I like the idea of being able to get my software in the hands of anyone in need of it; regardless of choices they’ve made.